diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8bf55d58dbd4..0d88dcbb2613 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,11 +8,10 @@ # those changes on build, release and publishing outcomes. * @MetaMask/extension-devs -**/snaps/** @MetaMask/snaps-devs development/ @MetaMask/extension-devs @kumavis lavamoat/ @MetaMask/extension-devs @MetaMask/supply-chain @MetaMask/snaps-devs -# The offscreen.ts script file that is included in the offscreedocument html +# The offscreen.ts script file that is included in the offscreen document html # file is responsible, at present, for loading the snaps execution environment # for MV3. Any changes to this file should require at least one member of the # snaps development team to review and approve the changes. @@ -84,3 +83,9 @@ ui/components/component-library @MetaMask/design-system-engineers # Slack handle: @accounts-team-devs | Slack channel: #metamask-accounts-team app/scripts/lib/snap-keyring @MetaMask/accounts-engineers + +# Snaps +**/snaps/** @MetaMask/snaps-devs +shared/constants/permissions.ts @MetaMask/snaps-devs +ui/helpers/utils/permission.js @MetaMask/snaps-devs +ui/hooks/useTransactionInsights.js @MetaMask/snaps-devs diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml new file mode 100644 index 000000000000..a45566089901 --- /dev/null +++ b/.github/workflows/run-integration-tests.yml @@ -0,0 +1,20 @@ +name: Run integration tests + +on: + push: + branches: [develop, master] + pull_request: + types: [opened,reopened,synchronize] + +jobs: + test-integration: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup environment + uses: ./.github/actions/setup-environment + + - name: test:integration:coverage + run: yarn test:integration:coverage diff --git a/.github/workflows/update-attributions.yml b/.github/workflows/update-attributions.yml index 0444ef345511..ca4dbeadeb13 100644 --- a/.github/workflows/update-attributions.yml +++ b/.github/workflows/update-attributions.yml @@ -39,7 +39,7 @@ jobs: -f content='+1' env: COMMENT_ID: ${{ github.event.comment.id }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} REPO: ${{ github.repository }} prepare: @@ -146,6 +146,8 @@ 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' ]] @@ -156,7 +158,7 @@ jobs: fi env: HAS_CHANGES: ${{ steps.attributions-changes.outputs.HAS_CHANGES }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} PR_NUMBER: ${{ github.event.issue.number }} check-status: @@ -192,6 +194,6 @@ jobs: gh pr comment "${PR_NUMBER}" --body "Attributions update failed. You can [review the logs or retry the attributions update here](${ACTION_RUN_URL})" fi env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} PR_NUMBER: ${{ github.event.issue.number }} ACTION_RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" diff --git a/.storybook/main.js b/.storybook/main.js index 3c67bb2b5ce1..554f6d955ee0 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -45,6 +45,9 @@ module.exports = { config.resolve.alias['../../../../store/actions'] = require.resolve( '../ui/__mocks__/actions.js', ); + config.resolve.alias['../../../../../../store/actions'] = require.resolve( + '../ui/__mocks__/actions.js', + ); config.resolve.fallback = { child_process: false, constants: false, diff --git a/.vscode/launch.json b/.vscode/launch.json index e801fb65f8b7..cd250eabebf7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -17,6 +17,22 @@ "program": "${workspaceFolder}/node_modules/jest/bin/jest" } }, + { + "type": "node", + "request": "launch", + "name": "Jest Integration: current file", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": [ + "${fileBasenameNoExtension}", + "--config", + "jest.integration.config.js" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest" + } + }, { "type": "node", "request": "launch", diff --git a/.yarn/patches/@metamask-gas-fee-controller-npm-15.1.2-db4d2976aa.patch b/.yarn/patches/@metamask-gas-fee-controller-npm-15.1.2-db4d2976aa.patch deleted file mode 100644 index e4e69c39609c..000000000000 --- a/.yarn/patches/@metamask-gas-fee-controller-npm-15.1.2-db4d2976aa.patch +++ /dev/null @@ -1,2630 +0,0 @@ -diff --git a/dist/GasFeeController.js b/dist/GasFeeController.js -index 089bba219bac937eeccb423f3e69ad5407ef9be0..b6bdda12ceeb0c38b344b20f80c3afc5120ce3f9 100644 ---- a/dist/GasFeeController.js -+++ b/dist/GasFeeController.js -@@ -3,12 +3,12 @@ - - - --var _chunkH5WHAYLIjs = require('./chunk-H5WHAYLI.js'); --require('./chunk-Q2YPK5SL.js'); -+var _chunkX74LQX2Yjs = require('./chunk-X74LQX2Y.js'); -+require('./chunk-2MFVV2BX.js'); - - - - - --exports.GAS_API_BASE_URL = _chunkH5WHAYLIjs.GAS_API_BASE_URL; exports.GAS_ESTIMATE_TYPES = _chunkH5WHAYLIjs.GAS_ESTIMATE_TYPES; exports.GasFeeController = _chunkH5WHAYLIjs.GasFeeController; exports.default = _chunkH5WHAYLIjs.GasFeeController_default; -+exports.GAS_ESTIMATE_TYPES = _chunkX74LQX2Yjs.GAS_ESTIMATE_TYPES; exports.GasFeeController = _chunkX74LQX2Yjs.GasFeeController; exports.LEGACY_GAS_PRICES_API_URL = _chunkX74LQX2Yjs.LEGACY_GAS_PRICES_API_URL; exports.default = _chunkX74LQX2Yjs.GasFeeController_default; - //# sourceMappingURL=GasFeeController.js.map -\ No newline at end of file -diff --git a/dist/GasFeeController.mjs b/dist/GasFeeController.mjs -index 14ab557e85665a30cbd8b4cec41448d5b88fed91..9b9b90786ac35a4cf320d00a933ef151cdf03821 100644 ---- a/dist/GasFeeController.mjs -+++ b/dist/GasFeeController.mjs -@@ -1,14 +1,14 @@ - import { -- GAS_API_BASE_URL, - GAS_ESTIMATE_TYPES, - GasFeeController, -- GasFeeController_default --} from "./chunk-BEVZS3YV.mjs"; --import "./chunk-KORLXV32.mjs"; -+ GasFeeController_default, -+ LEGACY_GAS_PRICES_API_URL -+} from "./chunk-A7NHJBXX.mjs"; -+import "./chunk-R3IOI7AK.mjs"; - export { -- GAS_API_BASE_URL, - GAS_ESTIMATE_TYPES, - GasFeeController, -+ LEGACY_GAS_PRICES_API_URL, - GasFeeController_default as default - }; - //# sourceMappingURL=GasFeeController.mjs.map -\ No newline at end of file -diff --git a/dist/chunk-2MFVV2BX.js b/dist/chunk-2MFVV2BX.js -new file mode 100644 -index 0000000000000000000000000000000000000000..4f83a713baa28f3d687c50ed7ad3c79715a2b588 ---- /dev/null -+++ b/dist/chunk-2MFVV2BX.js -@@ -0,0 +1,156 @@ -+"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }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/gas-util.ts -+ -+ -+ -+ -+ -+var _controllerutils = require('@metamask/controller-utils'); -+var _bnjs = require('bn.js'); var _bnjs2 = _interopRequireDefault(_bnjs); -+var makeClientIdHeader = (clientId) => ({ "X-Client-Id": clientId }); -+function normalizeGWEIDecimalNumbers(n) { -+ const numberAsWEIHex = _controllerutils.gweiDecToWEIBN.call(void 0, n).toString(16); -+ const numberAsGWEI = _controllerutils.weiHexToGweiDec.call(void 0, numberAsWEIHex); -+ return numberAsGWEI; -+} -+async function fetchGasEstimates(url, clientId) { -+ const estimates = await _controllerutils.handleFetch.call(void 0, -+ url, -+ clientId ? { headers: makeClientIdHeader(clientId) } : void 0 -+ ); -+ return { -+ low: { -+ ...estimates.low, -+ suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.low.suggestedMaxPriorityFeePerGas -+ ), -+ suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.low.suggestedMaxFeePerGas -+ ) -+ }, -+ medium: { -+ ...estimates.medium, -+ suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.medium.suggestedMaxPriorityFeePerGas -+ ), -+ suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.medium.suggestedMaxFeePerGas -+ ) -+ }, -+ high: { -+ ...estimates.high, -+ suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.high.suggestedMaxPriorityFeePerGas -+ ), -+ suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.high.suggestedMaxFeePerGas -+ ) -+ }, -+ estimatedBaseFee: normalizeGWEIDecimalNumbers(estimates.estimatedBaseFee), -+ historicalBaseFeeRange: estimates.historicalBaseFeeRange, -+ baseFeeTrend: estimates.baseFeeTrend, -+ latestPriorityFeeRange: estimates.latestPriorityFeeRange, -+ historicalPriorityFeeRange: estimates.historicalPriorityFeeRange, -+ priorityFeeTrend: estimates.priorityFeeTrend, -+ networkCongestion: estimates.networkCongestion -+ }; -+} -+async function fetchLegacyGasPriceEstimates(url, clientId) { -+ const result = await _controllerutils.handleFetch.call(void 0, url, { -+ referrer: url, -+ referrerPolicy: "no-referrer-when-downgrade", -+ method: "GET", -+ mode: "cors", -+ headers: { -+ "Content-Type": "application/json", -+ ...clientId && makeClientIdHeader(clientId) -+ } -+ }); -+ return { -+ low: result.SafeGasPrice, -+ medium: result.ProposeGasPrice, -+ high: result.FastGasPrice -+ }; -+} -+async function fetchEthGasPriceEstimate(ethQuery) { -+ const gasPrice = await _controllerutils.query.call(void 0, ethQuery, "gasPrice"); -+ return { -+ gasPrice: _controllerutils.weiHexToGweiDec.call(void 0, gasPrice).toString() -+ }; -+} -+function calculateTimeEstimate(maxPriorityFeePerGas, maxFeePerGas, gasFeeEstimates) { -+ const { low, medium, high, estimatedBaseFee } = gasFeeEstimates; -+ const maxPriorityFeePerGasInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, maxPriorityFeePerGas); -+ const maxFeePerGasInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, maxFeePerGas); -+ const estimatedBaseFeeInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, estimatedBaseFee); -+ const effectiveMaxPriorityFee = _bnjs2.default.min( -+ maxPriorityFeePerGasInWEI, -+ maxFeePerGasInWEI.sub(estimatedBaseFeeInWEI) -+ ); -+ const lowMaxPriorityFeeInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, -+ low.suggestedMaxPriorityFeePerGas -+ ); -+ const mediumMaxPriorityFeeInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, -+ medium.suggestedMaxPriorityFeePerGas -+ ); -+ const highMaxPriorityFeeInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, -+ high.suggestedMaxPriorityFeePerGas -+ ); -+ let lowerTimeBound; -+ let upperTimeBound; -+ if (effectiveMaxPriorityFee.lt(lowMaxPriorityFeeInWEI)) { -+ lowerTimeBound = null; -+ upperTimeBound = "unknown"; -+ } else if (effectiveMaxPriorityFee.gte(lowMaxPriorityFeeInWEI) && effectiveMaxPriorityFee.lt(mediumMaxPriorityFeeInWEI)) { -+ lowerTimeBound = low.minWaitTimeEstimate; -+ upperTimeBound = low.maxWaitTimeEstimate; -+ } else if (effectiveMaxPriorityFee.gte(mediumMaxPriorityFeeInWEI) && effectiveMaxPriorityFee.lt(highMaxPriorityFeeInWEI)) { -+ lowerTimeBound = medium.minWaitTimeEstimate; -+ upperTimeBound = medium.maxWaitTimeEstimate; -+ } else if (effectiveMaxPriorityFee.eq(highMaxPriorityFeeInWEI)) { -+ lowerTimeBound = high.minWaitTimeEstimate; -+ upperTimeBound = high.maxWaitTimeEstimate; -+ } else { -+ lowerTimeBound = 0; -+ upperTimeBound = high.maxWaitTimeEstimate; -+ } -+ return { -+ lowerTimeBound, -+ upperTimeBound -+ }; -+} -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+exports.__privateGet = __privateGet; exports.__privateAdd = __privateAdd; exports.__privateSet = __privateSet; exports.__privateMethod = __privateMethod; exports.normalizeGWEIDecimalNumbers = normalizeGWEIDecimalNumbers; exports.fetchGasEstimates = fetchGasEstimates; exports.fetchLegacyGasPriceEstimates = fetchLegacyGasPriceEstimates; exports.fetchEthGasPriceEstimate = fetchEthGasPriceEstimate; exports.calculateTimeEstimate = calculateTimeEstimate; -+//# sourceMappingURL=chunk-2MFVV2BX.js.map -\ No newline at end of file -diff --git a/dist/chunk-2MFVV2BX.js.map b/dist/chunk-2MFVV2BX.js.map -new file mode 100644 -index 0000000000000000000000000000000000000000..c6d83c1c168ee0b053be10a74334ac712bb6a6a1 ---- /dev/null -+++ b/dist/chunk-2MFVV2BX.js.map -@@ -0,0 +1 @@ -+{"version":3,"sources":["../src/gas-util.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n query,\n handleFetch,\n gweiDecToWEIBN,\n weiHexToGweiDec,\n} from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport BN from 'bn.js';\n\nimport type {\n GasFeeEstimates,\n EthGasPriceEstimate,\n EstimatedGasFeeTimeBounds,\n unknownString,\n LegacyGasPriceEstimate,\n} from './GasFeeController';\n\nconst makeClientIdHeader = (clientId: string) => ({ 'X-Client-Id': clientId });\n\n/**\n * Convert a decimal GWEI value to a decimal string rounded to the nearest WEI.\n *\n * @param n - The input GWEI amount, as a decimal string or a number.\n * @returns The decimal string GWEI amount.\n */\nexport function normalizeGWEIDecimalNumbers(n: string | number) {\n const numberAsWEIHex = gweiDecToWEIBN(n).toString(16);\n const numberAsGWEI = weiHexToGweiDec(numberAsWEIHex);\n return numberAsGWEI;\n}\n\n/**\n * Fetch gas estimates from the given URL.\n *\n * @param url - The gas estimate URL.\n * @param clientId - The client ID used to identify to the API who is asking for estimates.\n * @returns The gas estimates.\n */\nexport async function fetchGasEstimates(\n url: string,\n clientId?: string,\n): Promise {\n const estimates = await handleFetch(\n url,\n clientId ? { headers: makeClientIdHeader(clientId) } : undefined,\n );\n return {\n low: {\n ...estimates.low,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.low.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.low.suggestedMaxFeePerGas,\n ),\n },\n medium: {\n ...estimates.medium,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.medium.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.medium.suggestedMaxFeePerGas,\n ),\n },\n high: {\n ...estimates.high,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.high.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.high.suggestedMaxFeePerGas,\n ),\n },\n estimatedBaseFee: normalizeGWEIDecimalNumbers(estimates.estimatedBaseFee),\n historicalBaseFeeRange: estimates.historicalBaseFeeRange,\n baseFeeTrend: estimates.baseFeeTrend,\n latestPriorityFeeRange: estimates.latestPriorityFeeRange,\n historicalPriorityFeeRange: estimates.historicalPriorityFeeRange,\n priorityFeeTrend: estimates.priorityFeeTrend,\n networkCongestion: estimates.networkCongestion,\n };\n}\n\n/**\n * Hit the legacy MetaSwaps gasPrices estimate api and return the low, medium\n * high values from that API.\n *\n * @param url - The URL to fetch gas price estimates from.\n * @param clientId - The client ID used to identify to the API who is asking for estimates.\n * @returns The gas price estimates.\n */\nexport async function fetchLegacyGasPriceEstimates(\n url: string,\n clientId?: string,\n): Promise {\n const result = await handleFetch(url, {\n referrer: url,\n referrerPolicy: 'no-referrer-when-downgrade',\n method: 'GET',\n mode: 'cors',\n headers: {\n 'Content-Type': 'application/json',\n ...(clientId && makeClientIdHeader(clientId)),\n },\n });\n return {\n low: result.SafeGasPrice,\n medium: result.ProposeGasPrice,\n high: result.FastGasPrice,\n };\n}\n\n/**\n * Get a gas price estimate from the network using the `eth_gasPrice` method.\n *\n * @param ethQuery - The EthQuery instance to call the network with.\n * @returns A gas price estimate.\n */\nexport async function fetchEthGasPriceEstimate(\n ethQuery: EthQuery,\n): Promise {\n const gasPrice = await query(ethQuery, 'gasPrice');\n return {\n gasPrice: weiHexToGweiDec(gasPrice).toString(),\n };\n}\n\n/**\n * Estimate the time it will take for a transaction to be confirmed.\n *\n * @param maxPriorityFeePerGas - The max priority fee per gas.\n * @param maxFeePerGas - The max fee per gas.\n * @param gasFeeEstimates - The gas fee estimates.\n * @returns The estimated lower and upper bounds for when this transaction will be confirmed.\n */\nexport function calculateTimeEstimate(\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n gasFeeEstimates: GasFeeEstimates,\n): EstimatedGasFeeTimeBounds {\n const { low, medium, high, estimatedBaseFee } = gasFeeEstimates;\n\n const maxPriorityFeePerGasInWEI = gweiDecToWEIBN(maxPriorityFeePerGas);\n const maxFeePerGasInWEI = gweiDecToWEIBN(maxFeePerGas);\n const estimatedBaseFeeInWEI = gweiDecToWEIBN(estimatedBaseFee);\n\n const effectiveMaxPriorityFee = BN.min(\n maxPriorityFeePerGasInWEI,\n maxFeePerGasInWEI.sub(estimatedBaseFeeInWEI),\n );\n\n const lowMaxPriorityFeeInWEI = gweiDecToWEIBN(\n low.suggestedMaxPriorityFeePerGas,\n );\n const mediumMaxPriorityFeeInWEI = gweiDecToWEIBN(\n medium.suggestedMaxPriorityFeePerGas,\n );\n const highMaxPriorityFeeInWEI = gweiDecToWEIBN(\n high.suggestedMaxPriorityFeePerGas,\n );\n\n let lowerTimeBound;\n let upperTimeBound;\n\n if (effectiveMaxPriorityFee.lt(lowMaxPriorityFeeInWEI)) {\n lowerTimeBound = null;\n upperTimeBound = 'unknown' as unknownString;\n } else if (\n effectiveMaxPriorityFee.gte(lowMaxPriorityFeeInWEI) &&\n effectiveMaxPriorityFee.lt(mediumMaxPriorityFeeInWEI)\n ) {\n lowerTimeBound = low.minWaitTimeEstimate;\n upperTimeBound = low.maxWaitTimeEstimate;\n } else if (\n effectiveMaxPriorityFee.gte(mediumMaxPriorityFeeInWEI) &&\n effectiveMaxPriorityFee.lt(highMaxPriorityFeeInWEI)\n ) {\n lowerTimeBound = medium.minWaitTimeEstimate;\n upperTimeBound = medium.maxWaitTimeEstimate;\n } else if (effectiveMaxPriorityFee.eq(highMaxPriorityFeeInWEI)) {\n lowerTimeBound = high.minWaitTimeEstimate;\n upperTimeBound = high.maxWaitTimeEstimate;\n } else {\n lowerTimeBound = 0;\n upperTimeBound = high.maxWaitTimeEstimate;\n }\n\n return {\n lowerTimeBound,\n upperTimeBound,\n };\n}\n"]} -\ No newline at end of file -diff --git a/dist/chunk-A7NHJBXX.mjs b/dist/chunk-A7NHJBXX.mjs -new file mode 100644 -index 0000000000000000000000000000000000000000..1dd920c4a314888548db98885976b336d2fec79d ---- /dev/null -+++ b/dist/chunk-A7NHJBXX.mjs -@@ -0,0 +1,390 @@ -+import { -+ __privateAdd, -+ __privateGet, -+ __privateMethod, -+ __privateSet, -+ calculateTimeEstimate, -+ fetchEthGasPriceEstimate, -+ fetchGasEstimates, -+ fetchLegacyGasPriceEstimates -+} from "./chunk-R3IOI7AK.mjs"; -+ -+// src/GasFeeController.ts -+import { -+ convertHexToDecimal, -+ safelyExecute, -+ toHex -+} from "@metamask/controller-utils"; -+import EthQuery from "@metamask/eth-query"; -+import { StaticIntervalPollingController } from "@metamask/polling-controller"; -+import { v1 as random } from "uuid"; -+var LEGACY_GAS_PRICES_API_URL = `https://api.metaswap.codefi.network/gasPrices`; -+var GAS_ESTIMATE_TYPES = { -+ FEE_MARKET: "fee-market", -+ LEGACY: "legacy", -+ ETH_GASPRICE: "eth_gasPrice", -+ NONE: "none" -+}; -+var metadata = { -+ gasFeeEstimatesByChainId: { -+ persist: true, -+ anonymous: false -+ }, -+ gasFeeEstimates: { persist: true, anonymous: false }, -+ estimatedGasFeeTimeBounds: { persist: true, anonymous: false }, -+ gasEstimateType: { persist: true, anonymous: false }, -+ nonRPCGasFeeApisDisabled: { persist: true, anonymous: false } -+}; -+var name = "GasFeeController"; -+var defaultState = { -+ gasFeeEstimatesByChainId: {}, -+ gasFeeEstimates: {}, -+ estimatedGasFeeTimeBounds: {}, -+ gasEstimateType: GAS_ESTIMATE_TYPES.NONE, -+ nonRPCGasFeeApisDisabled: false -+}; -+var _getProvider, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn; -+var GasFeeController = class extends StaticIntervalPollingController { -+ /** -+ * Creates a GasFeeController instance. -+ * -+ * @param options - The controller options. -+ * @param options.interval - The time in milliseconds to wait between polls. -+ * @param options.messenger - The controller messenger. -+ * @param options.state - The initial state. -+ * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current -+ * network is EIP-1559 compatible. -+ * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the -+ * current network is compatible with the legacy gas price API. -+ * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current -+ * account is EIP-1559 compatible. -+ * @param options.getChainId - Returns the current chain ID. -+ * @param options.getProvider - Returns a network provider for the current network. -+ * @param options.onNetworkDidChange - A function for registering an event handler for the -+ * network state change event. -+ * @param options.legacyAPIEndpoint - The legacy gas price API URL. This option is primarily for -+ * testing purposes. -+ * @param options.EIP1559APIEndpoint - The EIP-1559 gas price API URL. -+ * @param options.clientId - The client ID used to identify to the gas estimation API who is -+ * asking for estimates. -+ */ -+ constructor({ -+ interval = 15e3, -+ messenger, -+ state, -+ getCurrentNetworkEIP1559Compatibility, -+ getCurrentAccountEIP1559Compatibility, -+ getChainId, -+ getCurrentNetworkLegacyGasAPICompatibility, -+ getProvider, -+ onNetworkDidChange, -+ legacyAPIEndpoint = LEGACY_GAS_PRICES_API_URL, -+ EIP1559APIEndpoint, -+ clientId -+ }) { -+ super({ -+ name, -+ metadata, -+ messenger, -+ state: { ...defaultState, ...state } -+ }); -+ __privateAdd(this, _onNetworkControllerDidChange); -+ __privateAdd(this, _getProvider, void 0); -+ this.intervalDelay = interval; -+ this.setIntervalLength(interval); -+ this.pollTokens = /* @__PURE__ */ new Set(); -+ this.getCurrentNetworkEIP1559Compatibility = getCurrentNetworkEIP1559Compatibility; -+ this.getCurrentNetworkLegacyGasAPICompatibility = getCurrentNetworkLegacyGasAPICompatibility; -+ this.getCurrentAccountEIP1559Compatibility = getCurrentAccountEIP1559Compatibility; -+ __privateSet(this, _getProvider, getProvider); -+ this.EIP1559APIEndpoint = EIP1559APIEndpoint; -+ this.legacyAPIEndpoint = legacyAPIEndpoint; -+ this.clientId = clientId; -+ this.ethQuery = new EthQuery(__privateGet(this, _getProvider).call(this)); -+ if (onNetworkDidChange && getChainId) { -+ this.currentChainId = getChainId(); -+ onNetworkDidChange(async (networkControllerState) => { -+ await __privateMethod(this, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn).call(this, networkControllerState); -+ }); -+ } else { -+ this.currentChainId = this.messagingSystem.call( -+ "NetworkController:getState" -+ ).providerConfig.chainId; -+ this.messagingSystem.subscribe( -+ "NetworkController:networkDidChange", -+ async (networkControllerState) => { -+ await __privateMethod(this, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn).call(this, networkControllerState); -+ } -+ ); -+ } -+ } -+ async resetPolling() { -+ if (this.pollTokens.size !== 0) { -+ const tokens = Array.from(this.pollTokens); -+ this.stopPolling(); -+ await this.getGasFeeEstimatesAndStartPolling(tokens[0]); -+ tokens.slice(1).forEach((token) => { -+ this.pollTokens.add(token); -+ }); -+ } -+ } -+ async fetchGasFeeEstimates(options) { -+ return await this._fetchGasFeeEstimateData(options); -+ } -+ async getGasFeeEstimatesAndStartPolling(pollToken) { -+ const _pollToken = pollToken || random(); -+ this.pollTokens.add(_pollToken); -+ if (this.pollTokens.size === 1) { -+ await this._fetchGasFeeEstimateData(); -+ this._poll(); -+ } -+ return _pollToken; -+ } -+ /** -+ * Gets and sets gasFeeEstimates in state. -+ * -+ * @param options - The gas fee estimate options. -+ * @param options.shouldUpdateState - Determines whether the state should be updated with the -+ * updated gas estimates. -+ * @returns The gas fee estimates. -+ */ -+ async _fetchGasFeeEstimateData(options = {}) { -+ const { shouldUpdateState = true, networkClientId } = options; -+ let ethQuery, isEIP1559Compatible, isLegacyGasAPICompatible, decimalChainId; -+ if (networkClientId !== void 0) { -+ const networkClient = this.messagingSystem.call( -+ "NetworkController:getNetworkClientById", -+ networkClientId -+ ); -+ isLegacyGasAPICompatible = networkClient.configuration.chainId === "0x38"; -+ decimalChainId = convertHexToDecimal(networkClient.configuration.chainId); -+ try { -+ const result = await this.messagingSystem.call( -+ "NetworkController:getEIP1559Compatibility", -+ networkClientId -+ ); -+ isEIP1559Compatible = result || false; -+ } catch { -+ isEIP1559Compatible = false; -+ } -+ ethQuery = new EthQuery(networkClient.provider); -+ } -+ ethQuery ?? (ethQuery = this.ethQuery); -+ isLegacyGasAPICompatible ?? (isLegacyGasAPICompatible = this.getCurrentNetworkLegacyGasAPICompatibility()); -+ decimalChainId ?? (decimalChainId = convertHexToDecimal(this.currentChainId)); -+ try { -+ isEIP1559Compatible ?? (isEIP1559Compatible = await this.getEIP1559Compatibility()); -+ } catch (e) { -+ console.error(e); -+ isEIP1559Compatible ?? (isEIP1559Compatible = false); -+ } -+ const gasFeeCalculations = await determineGasFeeCalculations({ -+ isEIP1559Compatible, -+ isLegacyGasAPICompatible, -+ fetchGasEstimates, -+ fetchGasEstimatesUrl: this.EIP1559APIEndpoint.replace( -+ "", -+ `${decimalChainId}` -+ ), -+ fetchLegacyGasPriceEstimates, -+ fetchLegacyGasPriceEstimatesUrl: this.legacyAPIEndpoint.replace( -+ "", -+ `${decimalChainId}` -+ ), -+ fetchEthGasPriceEstimate, -+ calculateTimeEstimate, -+ clientId: this.clientId, -+ ethQuery, -+ nonRPCGasFeeApisDisabled: this.state.nonRPCGasFeeApisDisabled -+ }); -+ if (shouldUpdateState) { -+ const chainId = toHex(decimalChainId); -+ this.update((state) => { -+ if (this.currentChainId === chainId) { -+ state.gasFeeEstimates = gasFeeCalculations.gasFeeEstimates; -+ state.estimatedGasFeeTimeBounds = gasFeeCalculations.estimatedGasFeeTimeBounds; -+ state.gasEstimateType = gasFeeCalculations.gasEstimateType; -+ } -+ state.gasFeeEstimatesByChainId ?? (state.gasFeeEstimatesByChainId = {}); -+ state.gasFeeEstimatesByChainId[chainId] = { -+ gasFeeEstimates: gasFeeCalculations.gasFeeEstimates, -+ estimatedGasFeeTimeBounds: gasFeeCalculations.estimatedGasFeeTimeBounds, -+ gasEstimateType: gasFeeCalculations.gasEstimateType -+ }; -+ }); -+ } -+ return gasFeeCalculations; -+ } -+ /** -+ * Remove the poll token, and stop polling if the set of poll tokens is empty. -+ * -+ * @param pollToken - The poll token to disconnect. -+ */ -+ disconnectPoller(pollToken) { -+ this.pollTokens.delete(pollToken); -+ if (this.pollTokens.size === 0) { -+ this.stopPolling(); -+ } -+ } -+ stopPolling() { -+ if (this.intervalId) { -+ clearInterval(this.intervalId); -+ } -+ this.pollTokens.clear(); -+ this.resetState(); -+ } -+ /** -+ * Prepare to discard this controller. -+ * -+ * This stops any active polling. -+ */ -+ destroy() { -+ super.destroy(); -+ this.stopPolling(); -+ } -+ _poll() { -+ if (this.intervalId) { -+ clearInterval(this.intervalId); -+ } -+ this.intervalId = setInterval(async () => { -+ await safelyExecute(() => this._fetchGasFeeEstimateData()); -+ }, this.intervalDelay); -+ } -+ /** -+ * Fetching token list from the Token Service API. -+ * -+ * @private -+ * @param networkClientId - The ID of the network client triggering the fetch. -+ * @returns A promise that resolves when this operation completes. -+ */ -+ async _executePoll(networkClientId) { -+ await this._fetchGasFeeEstimateData({ networkClientId }); -+ } -+ resetState() { -+ this.update(() => { -+ return defaultState; -+ }); -+ } -+ async getEIP1559Compatibility() { -+ const currentNetworkIsEIP1559Compatible = await this.getCurrentNetworkEIP1559Compatibility(); -+ const currentAccountIsEIP1559Compatible = this.getCurrentAccountEIP1559Compatibility?.() ?? true; -+ return currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible; -+ } -+ getTimeEstimate(maxPriorityFeePerGas, maxFeePerGas) { -+ if (!this.state.gasFeeEstimates || this.state.gasEstimateType !== GAS_ESTIMATE_TYPES.FEE_MARKET) { -+ return {}; -+ } -+ return calculateTimeEstimate( -+ maxPriorityFeePerGas, -+ maxFeePerGas, -+ this.state.gasFeeEstimates -+ ); -+ } -+ enableNonRPCGasFeeApis() { -+ this.update((state) => { -+ state.nonRPCGasFeeApisDisabled = false; -+ }); -+ } -+ disableNonRPCGasFeeApis() { -+ this.update((state) => { -+ state.nonRPCGasFeeApisDisabled = true; -+ }); -+ } -+}; -+_getProvider = new WeakMap(); -+_onNetworkControllerDidChange = new WeakSet(); -+onNetworkControllerDidChange_fn = async function(networkControllerState) { -+ const newChainId = networkControllerState.providerConfig.chainId; -+ if (newChainId !== this.currentChainId) { -+ this.ethQuery = new EthQuery(__privateGet(this, _getProvider).call(this)); -+ await this.resetPolling(); -+ this.currentChainId = newChainId; -+ } -+}; -+var GasFeeController_default = GasFeeController; -+ -+// src/determineGasFeeCalculations.ts -+async function determineGasFeeCalculations(args) { -+ try { -+ return await getEstimatesUsingFallbacks(args); -+ } catch (error) { -+ if (error instanceof Error) { -+ throw new Error( -+ `Gas fee/price estimation failed. Message: ${error.message}` -+ ); -+ } -+ throw error; -+ } -+} -+async function getEstimatesUsingFallbacks(request) { -+ const { -+ isEIP1559Compatible, -+ isLegacyGasAPICompatible, -+ nonRPCGasFeeApisDisabled -+ } = request; -+ try { -+ if (isEIP1559Compatible && !nonRPCGasFeeApisDisabled) { -+ return await getEstimatesUsingFeeMarketEndpoint(request); -+ } -+ if (isLegacyGasAPICompatible && !nonRPCGasFeeApisDisabled) { -+ return await getEstimatesUsingLegacyEndpoint(request); -+ } -+ throw new Error("Main gas fee/price estimation failed. Use fallback"); -+ } catch { -+ return await getEstimatesUsingProvider(request); -+ } -+} -+async function getEstimatesUsingFeeMarketEndpoint(request) { -+ const { -+ fetchGasEstimates: fetchGasEstimates2, -+ fetchGasEstimatesUrl, -+ clientId, -+ calculateTimeEstimate: calculateTimeEstimate2 -+ } = request; -+ const estimates = await fetchGasEstimates2(fetchGasEstimatesUrl, clientId); -+ const { suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas } = estimates.medium; -+ const estimatedGasFeeTimeBounds = calculateTimeEstimate2( -+ suggestedMaxPriorityFeePerGas, -+ suggestedMaxFeePerGas, -+ estimates -+ ); -+ return { -+ gasFeeEstimates: estimates, -+ estimatedGasFeeTimeBounds, -+ gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET -+ }; -+} -+async function getEstimatesUsingLegacyEndpoint(request) { -+ const { -+ fetchLegacyGasPriceEstimates: fetchLegacyGasPriceEstimates2, -+ fetchLegacyGasPriceEstimatesUrl, -+ clientId -+ } = request; -+ const estimates = await fetchLegacyGasPriceEstimates2( -+ fetchLegacyGasPriceEstimatesUrl, -+ clientId -+ ); -+ return { -+ gasFeeEstimates: estimates, -+ estimatedGasFeeTimeBounds: {}, -+ gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY -+ }; -+} -+async function getEstimatesUsingProvider(request) { -+ const { ethQuery, fetchEthGasPriceEstimate: fetchEthGasPriceEstimate2 } = request; -+ const estimates = await fetchEthGasPriceEstimate2(ethQuery); -+ return { -+ gasFeeEstimates: estimates, -+ estimatedGasFeeTimeBounds: {}, -+ gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE -+ }; -+} -+ -+export { -+ determineGasFeeCalculations, -+ LEGACY_GAS_PRICES_API_URL, -+ GAS_ESTIMATE_TYPES, -+ GasFeeController, -+ GasFeeController_default -+}; -+//# sourceMappingURL=chunk-A7NHJBXX.mjs.map -\ No newline at end of file -diff --git a/dist/chunk-A7NHJBXX.mjs.map b/dist/chunk-A7NHJBXX.mjs.map -new file mode 100644 -index 0000000000000000000000000000000000000000..d40e21b8870bb3ef2d3a092e9abdbb83527764f3 ---- /dev/null -+++ b/dist/chunk-A7NHJBXX.mjs.map -@@ -0,0 +1 @@ -+{"version":3,"sources":["../src/GasFeeController.ts","../src/determineGasFeeCalculations.ts"],"sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport {\n convertHexToDecimal,\n safelyExecute,\n toHex,\n} from '@metamask/controller-utils';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClientId,\n NetworkControllerGetEIP1559CompatibilityAction,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerNetworkDidChangeEvent,\n NetworkState,\n ProviderProxy,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { Hex } from '@metamask/utils';\nimport { v1 as random } from 'uuid';\n\nimport determineGasFeeCalculations from './determineGasFeeCalculations';\nimport {\n fetchGasEstimates,\n fetchLegacyGasPriceEstimates,\n fetchEthGasPriceEstimate,\n calculateTimeEstimate,\n} from './gas-util';\n\nexport const LEGACY_GAS_PRICES_API_URL = `https://api.metaswap.codefi.network/gasPrices`;\n\nexport type unknownString = 'unknown';\n\n// Fee Market describes the way gas is set after the london hardfork, and was\n// defined by EIP-1559.\nexport type FeeMarketEstimateType = 'fee-market';\n// Legacy describes gasPrice estimates from before london hardfork, when the\n// user is connected to mainnet and are presented with fast/average/slow\n// estimate levels to choose from.\nexport type LegacyEstimateType = 'legacy';\n// EthGasPrice describes a gasPrice estimate received from eth_gasPrice. Post\n// london this value should only be used for legacy type transactions when on\n// networks that support EIP-1559. This type of estimate is the most accurate\n// to display on custom networks that don't support EIP-1559.\nexport type EthGasPriceEstimateType = 'eth_gasPrice';\n// NoEstimate describes the state of the controller before receiving its first\n// estimate.\nexport type NoEstimateType = 'none';\n\n/**\n * Indicates which type of gasEstimate the controller is currently returning.\n * This is useful as a way of asserting that the shape of gasEstimates matches\n * expectations. NONE is a special case indicating that no previous gasEstimate\n * has been fetched.\n */\nexport const GAS_ESTIMATE_TYPES = {\n FEE_MARKET: 'fee-market' as FeeMarketEstimateType,\n LEGACY: 'legacy' as LegacyEstimateType,\n ETH_GASPRICE: 'eth_gasPrice' as EthGasPriceEstimateType,\n NONE: 'none' as NoEstimateType,\n};\n\nexport type GasEstimateType =\n | FeeMarketEstimateType\n | EthGasPriceEstimateType\n | LegacyEstimateType\n | NoEstimateType;\n\nexport type EstimatedGasFeeTimeBounds = {\n lowerTimeBound: number | null;\n upperTimeBound: number | unknownString;\n};\n\n/**\n * @type EthGasPriceEstimate\n *\n * A single gas price estimate for networks and accounts that don't support EIP-1559\n * This estimate comes from eth_gasPrice but is converted to dec gwei to match other\n * return values\n * @property gasPrice - A GWEI dec string\n */\n\nexport type EthGasPriceEstimate = {\n gasPrice: string;\n};\n\n/**\n * @type LegacyGasPriceEstimate\n *\n * A set of gas price estimates for networks and accounts that don't support EIP-1559\n * These estimates include low, medium and high all as strings representing gwei in\n * decimal format.\n * @property high - gasPrice, in decimal gwei string format, suggested for fast inclusion\n * @property medium - gasPrice, in decimal gwei string format, suggested for avg inclusion\n * @property low - gasPrice, in decimal gwei string format, suggested for slow inclusion\n */\nexport type LegacyGasPriceEstimate = {\n high: string;\n medium: string;\n low: string;\n};\n\n/**\n * @type Eip1559GasFee\n *\n * Data necessary to provide an estimate of a gas fee with a specific tip\n * @property minWaitTimeEstimate - The fastest the transaction will take, in milliseconds\n * @property maxWaitTimeEstimate - The slowest the transaction will take, in milliseconds\n * @property suggestedMaxPriorityFeePerGas - A suggested \"tip\", a GWEI hex number\n * @property suggestedMaxFeePerGas - A suggested max fee, the most a user will pay. a GWEI hex number\n */\nexport type Eip1559GasFee = {\n minWaitTimeEstimate: number; // a time duration in milliseconds\n maxWaitTimeEstimate: number; // a time duration in milliseconds\n suggestedMaxPriorityFeePerGas: string; // a GWEI decimal number\n suggestedMaxFeePerGas: string; // a GWEI decimal number\n};\n\n/**\n * @type GasFeeEstimates\n *\n * Data necessary to provide multiple GasFee estimates, and supporting information, to the user\n * @property low - A GasFee for a minimum necessary combination of tip and maxFee\n * @property medium - A GasFee for a recommended combination of tip and maxFee\n * @property high - A GasFee for a high combination of tip and maxFee\n * @property estimatedBaseFee - An estimate of what the base fee will be for the pending/next block. A GWEI dec number\n * @property networkCongestion - A normalized number that can be used to gauge the congestion\n * level of the network, with 0 meaning not congested and 1 meaning extremely congested\n */\nexport type GasFeeEstimates = SourcedGasFeeEstimates | FallbackGasFeeEstimates;\n\ntype SourcedGasFeeEstimates = {\n low: Eip1559GasFee;\n medium: Eip1559GasFee;\n high: Eip1559GasFee;\n estimatedBaseFee: string;\n historicalBaseFeeRange: [string, string];\n baseFeeTrend: 'up' | 'down' | 'level';\n latestPriorityFeeRange: [string, string];\n historicalPriorityFeeRange: [string, string];\n priorityFeeTrend: 'up' | 'down' | 'level';\n networkCongestion: number;\n};\n\ntype FallbackGasFeeEstimates = {\n low: Eip1559GasFee;\n medium: Eip1559GasFee;\n high: Eip1559GasFee;\n estimatedBaseFee: string;\n historicalBaseFeeRange: null;\n baseFeeTrend: null;\n latestPriorityFeeRange: null;\n historicalPriorityFeeRange: null;\n priorityFeeTrend: null;\n networkCongestion: null;\n};\n\nconst metadata = {\n gasFeeEstimatesByChainId: {\n persist: true,\n anonymous: false,\n },\n gasFeeEstimates: { persist: true, anonymous: false },\n estimatedGasFeeTimeBounds: { persist: true, anonymous: false },\n gasEstimateType: { persist: true, anonymous: false },\n nonRPCGasFeeApisDisabled: { persist: true, anonymous: false },\n};\n\nexport type GasFeeStateEthGasPrice = {\n gasFeeEstimates: EthGasPriceEstimate;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: EthGasPriceEstimateType;\n};\n\nexport type GasFeeStateFeeMarket = {\n gasFeeEstimates: GasFeeEstimates;\n estimatedGasFeeTimeBounds: EstimatedGasFeeTimeBounds | Record;\n gasEstimateType: FeeMarketEstimateType;\n};\n\nexport type GasFeeStateLegacy = {\n gasFeeEstimates: LegacyGasPriceEstimate;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: LegacyEstimateType;\n};\n\nexport type GasFeeStateNoEstimates = {\n gasFeeEstimates: Record;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: NoEstimateType;\n};\n\nexport type FetchGasFeeEstimateOptions = {\n shouldUpdateState?: boolean;\n networkClientId?: NetworkClientId;\n};\n\n/**\n * @type GasFeeState\n *\n * Gas Fee controller state\n * @property gasFeeEstimates - Gas fee estimate data based on new EIP-1559 properties\n * @property estimatedGasFeeTimeBounds - Estimates representing the minimum and maximum\n */\nexport type SingleChainGasFeeState =\n | GasFeeStateEthGasPrice\n | GasFeeStateFeeMarket\n | GasFeeStateLegacy\n | GasFeeStateNoEstimates;\n\nexport type GasFeeEstimatesByChainId = {\n gasFeeEstimatesByChainId?: Record;\n};\n\nexport type GasFeeState = GasFeeEstimatesByChainId &\n SingleChainGasFeeState & {\n nonRPCGasFeeApisDisabled?: boolean;\n };\n\nconst name = 'GasFeeController';\n\nexport type GasFeeStateChange = ControllerStateChangeEvent<\n typeof name,\n GasFeeState\n>;\n\nexport type GetGasFeeState = ControllerGetStateAction;\n\nexport type GasFeeControllerActions = GetGasFeeState;\n\nexport type GasFeeControllerEvents = GasFeeStateChange;\n\ntype AllowedActions =\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetEIP1559CompatibilityAction;\n\ntype GasFeeMessenger = RestrictedControllerMessenger<\n typeof name,\n GasFeeControllerActions | AllowedActions,\n GasFeeControllerEvents | NetworkControllerNetworkDidChangeEvent,\n AllowedActions['type'],\n NetworkControllerNetworkDidChangeEvent['type']\n>;\n\nconst defaultState: GasFeeState = {\n gasFeeEstimatesByChainId: {},\n gasFeeEstimates: {},\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.NONE,\n nonRPCGasFeeApisDisabled: false,\n};\n\n/**\n * Controller that retrieves gas fee estimate data and polls for updated data on a set interval\n */\nexport class GasFeeController extends StaticIntervalPollingController<\n typeof name,\n GasFeeState,\n GasFeeMessenger\n> {\n private intervalId?: ReturnType;\n\n private readonly intervalDelay;\n\n private readonly pollTokens: Set;\n\n private readonly legacyAPIEndpoint: string;\n\n private readonly EIP1559APIEndpoint: string;\n\n private readonly getCurrentNetworkEIP1559Compatibility;\n\n private readonly getCurrentNetworkLegacyGasAPICompatibility;\n\n private readonly getCurrentAccountEIP1559Compatibility;\n\n private currentChainId;\n\n private ethQuery?: EthQuery;\n\n private readonly clientId?: string;\n\n #getProvider: () => ProviderProxy;\n\n /**\n * Creates a GasFeeController instance.\n *\n * @param options - The controller options.\n * @param options.interval - The time in milliseconds to wait between polls.\n * @param options.messenger - The controller messenger.\n * @param options.state - The initial state.\n * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current\n * network is EIP-1559 compatible.\n * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the\n * current network is compatible with the legacy gas price API.\n * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current\n * account is EIP-1559 compatible.\n * @param options.getChainId - Returns the current chain ID.\n * @param options.getProvider - Returns a network provider for the current network.\n * @param options.onNetworkDidChange - A function for registering an event handler for the\n * network state change event.\n * @param options.legacyAPIEndpoint - The legacy gas price API URL. This option is primarily for\n * testing purposes.\n * @param options.EIP1559APIEndpoint - The EIP-1559 gas price API URL.\n * @param options.clientId - The client ID used to identify to the gas estimation API who is\n * asking for estimates.\n */\n constructor({\n interval = 15000,\n messenger,\n state,\n getCurrentNetworkEIP1559Compatibility,\n getCurrentAccountEIP1559Compatibility,\n getChainId,\n getCurrentNetworkLegacyGasAPICompatibility,\n getProvider,\n onNetworkDidChange,\n legacyAPIEndpoint = LEGACY_GAS_PRICES_API_URL,\n EIP1559APIEndpoint,\n clientId,\n }: {\n interval?: number;\n messenger: GasFeeMessenger;\n state?: GasFeeState;\n getCurrentNetworkEIP1559Compatibility: () => Promise;\n getCurrentNetworkLegacyGasAPICompatibility: () => boolean;\n getCurrentAccountEIP1559Compatibility?: () => boolean;\n getChainId?: () => Hex;\n getProvider: () => ProviderProxy;\n onNetworkDidChange?: (listener: (state: NetworkState) => void) => void;\n legacyAPIEndpoint?: string;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n EIP1559APIEndpoint: string;\n clientId?: string;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.intervalDelay = interval;\n this.setIntervalLength(interval);\n this.pollTokens = new Set();\n this.getCurrentNetworkEIP1559Compatibility =\n getCurrentNetworkEIP1559Compatibility;\n this.getCurrentNetworkLegacyGasAPICompatibility =\n getCurrentNetworkLegacyGasAPICompatibility;\n this.getCurrentAccountEIP1559Compatibility =\n getCurrentAccountEIP1559Compatibility;\n this.#getProvider = getProvider;\n this.EIP1559APIEndpoint = EIP1559APIEndpoint;\n this.legacyAPIEndpoint = legacyAPIEndpoint;\n this.clientId = clientId;\n\n this.ethQuery = new EthQuery(this.#getProvider());\n\n if (onNetworkDidChange && getChainId) {\n this.currentChainId = getChainId();\n onNetworkDidChange(async (networkControllerState) => {\n await this.#onNetworkControllerDidChange(networkControllerState);\n });\n } else {\n this.currentChainId = this.messagingSystem.call(\n 'NetworkController:getState',\n ).providerConfig.chainId;\n this.messagingSystem.subscribe(\n 'NetworkController:networkDidChange',\n async (networkControllerState) => {\n await this.#onNetworkControllerDidChange(networkControllerState);\n },\n );\n }\n }\n\n async resetPolling() {\n if (this.pollTokens.size !== 0) {\n const tokens = Array.from(this.pollTokens);\n this.stopPolling();\n await this.getGasFeeEstimatesAndStartPolling(tokens[0]);\n tokens.slice(1).forEach((token) => {\n this.pollTokens.add(token);\n });\n }\n }\n\n async fetchGasFeeEstimates(options?: FetchGasFeeEstimateOptions) {\n return await this._fetchGasFeeEstimateData(options);\n }\n\n async getGasFeeEstimatesAndStartPolling(\n pollToken: string | undefined,\n ): Promise {\n const _pollToken = pollToken || random();\n\n this.pollTokens.add(_pollToken);\n\n if (this.pollTokens.size === 1) {\n await this._fetchGasFeeEstimateData();\n this._poll();\n }\n\n return _pollToken;\n }\n\n /**\n * Gets and sets gasFeeEstimates in state.\n *\n * @param options - The gas fee estimate options.\n * @param options.shouldUpdateState - Determines whether the state should be updated with the\n * updated gas estimates.\n * @returns The gas fee estimates.\n */\n async _fetchGasFeeEstimateData(\n options: FetchGasFeeEstimateOptions = {},\n ): Promise {\n const { shouldUpdateState = true, networkClientId } = options;\n\n let ethQuery,\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n decimalChainId: number;\n\n if (networkClientId !== undefined) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n isLegacyGasAPICompatible = networkClient.configuration.chainId === '0x38';\n\n decimalChainId = convertHexToDecimal(networkClient.configuration.chainId);\n\n try {\n const result = await this.messagingSystem.call(\n 'NetworkController:getEIP1559Compatibility',\n networkClientId,\n );\n isEIP1559Compatible = result || false;\n } catch {\n isEIP1559Compatible = false;\n }\n ethQuery = new EthQuery(networkClient.provider);\n }\n\n ethQuery ??= this.ethQuery;\n\n isLegacyGasAPICompatible ??=\n this.getCurrentNetworkLegacyGasAPICompatibility();\n\n decimalChainId ??= convertHexToDecimal(this.currentChainId);\n\n try {\n isEIP1559Compatible ??= await this.getEIP1559Compatibility();\n } catch (e) {\n console.error(e);\n isEIP1559Compatible ??= false;\n }\n\n const gasFeeCalculations = await determineGasFeeCalculations({\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n fetchGasEstimates,\n fetchGasEstimatesUrl: this.EIP1559APIEndpoint.replace(\n '',\n `${decimalChainId}`,\n ),\n fetchLegacyGasPriceEstimates,\n fetchLegacyGasPriceEstimatesUrl: this.legacyAPIEndpoint.replace(\n '',\n `${decimalChainId}`,\n ),\n fetchEthGasPriceEstimate,\n calculateTimeEstimate,\n clientId: this.clientId,\n ethQuery,\n nonRPCGasFeeApisDisabled: this.state.nonRPCGasFeeApisDisabled,\n });\n\n if (shouldUpdateState) {\n const chainId = toHex(decimalChainId);\n this.update((state) => {\n if (this.currentChainId === chainId) {\n state.gasFeeEstimates = gasFeeCalculations.gasFeeEstimates;\n state.estimatedGasFeeTimeBounds =\n gasFeeCalculations.estimatedGasFeeTimeBounds;\n state.gasEstimateType = gasFeeCalculations.gasEstimateType;\n }\n state.gasFeeEstimatesByChainId ??= {};\n state.gasFeeEstimatesByChainId[chainId] = {\n gasFeeEstimates: gasFeeCalculations.gasFeeEstimates,\n estimatedGasFeeTimeBounds:\n gasFeeCalculations.estimatedGasFeeTimeBounds,\n gasEstimateType: gasFeeCalculations.gasEstimateType,\n } as SingleChainGasFeeState;\n });\n }\n\n return gasFeeCalculations;\n }\n\n /**\n * Remove the poll token, and stop polling if the set of poll tokens is empty.\n *\n * @param pollToken - The poll token to disconnect.\n */\n disconnectPoller(pollToken: string) {\n this.pollTokens.delete(pollToken);\n if (this.pollTokens.size === 0) {\n this.stopPolling();\n }\n }\n\n stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n this.pollTokens.clear();\n this.resetState();\n }\n\n /**\n * Prepare to discard this controller.\n *\n * This stops any active polling.\n */\n override destroy() {\n super.destroy();\n this.stopPolling();\n }\n\n private _poll() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n\n this.intervalId = setInterval(async () => {\n await safelyExecute(() => this._fetchGasFeeEstimateData());\n }, this.intervalDelay);\n }\n\n /**\n * Fetching token list from the Token Service API.\n *\n * @private\n * @param networkClientId - The ID of the network client triggering the fetch.\n * @returns A promise that resolves when this operation completes.\n */\n async _executePoll(networkClientId: string): Promise {\n await this._fetchGasFeeEstimateData({ networkClientId });\n }\n\n private resetState() {\n this.update(() => {\n return defaultState;\n });\n }\n\n private async getEIP1559Compatibility() {\n const currentNetworkIsEIP1559Compatible =\n await this.getCurrentNetworkEIP1559Compatibility();\n const currentAccountIsEIP1559Compatible =\n this.getCurrentAccountEIP1559Compatibility?.() ?? true;\n\n return (\n currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible\n );\n }\n\n getTimeEstimate(\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n ): EstimatedGasFeeTimeBounds | Record {\n if (\n !this.state.gasFeeEstimates ||\n this.state.gasEstimateType !== GAS_ESTIMATE_TYPES.FEE_MARKET\n ) {\n return {};\n }\n return calculateTimeEstimate(\n maxPriorityFeePerGas,\n maxFeePerGas,\n this.state.gasFeeEstimates,\n );\n }\n\n async #onNetworkControllerDidChange(networkControllerState: NetworkState) {\n const newChainId = networkControllerState.providerConfig.chainId;\n\n if (newChainId !== this.currentChainId) {\n this.ethQuery = new EthQuery(this.#getProvider());\n await this.resetPolling();\n\n this.currentChainId = newChainId;\n }\n }\n\n enableNonRPCGasFeeApis() {\n this.update((state) => {\n state.nonRPCGasFeeApisDisabled = false;\n });\n }\n\n disableNonRPCGasFeeApis() {\n this.update((state) => {\n state.nonRPCGasFeeApisDisabled = true;\n });\n }\n}\n\nexport default GasFeeController;\n","import type {\n EstimatedGasFeeTimeBounds,\n EthGasPriceEstimate,\n GasFeeEstimates,\n GasFeeState as GasFeeCalculations,\n LegacyGasPriceEstimate,\n} from './GasFeeController';\nimport { GAS_ESTIMATE_TYPES } from './GasFeeController';\n\ntype DetermineGasFeeCalculationsRequest = {\n isEIP1559Compatible: boolean;\n isLegacyGasAPICompatible: boolean;\n fetchGasEstimates: (\n url: string,\n clientId?: string,\n ) => Promise;\n fetchGasEstimatesUrl: string;\n fetchLegacyGasPriceEstimates: (\n url: string,\n clientId?: string,\n ) => Promise;\n fetchLegacyGasPriceEstimatesUrl: string;\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n fetchEthGasPriceEstimate: (ethQuery: any) => Promise;\n calculateTimeEstimate: (\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n gasFeeEstimates: GasFeeEstimates,\n ) => EstimatedGasFeeTimeBounds;\n clientId: string | undefined;\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ethQuery: any;\n nonRPCGasFeeApisDisabled?: boolean;\n};\n\n/**\n * Obtains a set of max base and priority fee estimates along with time estimates so that we\n * can present them to users when they are sending transactions or making swaps.\n *\n * @param args - The arguments.\n * @param args.isEIP1559Compatible - Governs whether or not we can use an EIP-1559-only method to\n * produce estimates.\n * @param args.isLegacyGasAPICompatible - Governs whether or not we can use a non-EIP-1559 method to\n * produce estimates (for instance, testnets do not support estimates altogether).\n * @param args.fetchGasEstimates - A function that fetches gas estimates using an EIP-1559-specific\n * API.\n * @param args.fetchGasEstimatesUrl - The URL for the API we can use to obtain EIP-1559-specific\n * estimates.\n * @param args.fetchLegacyGasPriceEstimates - A function that fetches gas estimates using an\n * non-EIP-1559-specific API.\n * @param args.fetchLegacyGasPriceEstimatesUrl - The URL for the API we can use to obtain\n * non-EIP-1559-specific estimates.\n * @param args.fetchEthGasPriceEstimate - A function that fetches gas estimates using\n * `eth_gasPrice`.\n * @param args.calculateTimeEstimate - A function that determine time estimate bounds.\n * @param args.clientId - An identifier that an API can use to know who is asking for estimates.\n * @param args.ethQuery - An EthQuery instance we can use to talk to Ethereum directly.\n * @param args.nonRPCGasFeeApisDisabled - Whether to disable requests to the legacyAPIEndpoint and the EIP1559APIEndpoint\n * @returns The gas fee calculations.\n */\nexport default async function determineGasFeeCalculations(\n args: DetermineGasFeeCalculationsRequest,\n): Promise {\n try {\n return await getEstimatesUsingFallbacks(args);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(\n `Gas fee/price estimation failed. Message: ${error.message}`,\n );\n }\n\n throw error;\n }\n}\n\n/**\n * Retrieve the gas fee estimates using a series of fallback mechanisms.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingFallbacks(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n nonRPCGasFeeApisDisabled,\n } = request;\n\n try {\n if (isEIP1559Compatible && !nonRPCGasFeeApisDisabled) {\n return await getEstimatesUsingFeeMarketEndpoint(request);\n }\n\n if (isLegacyGasAPICompatible && !nonRPCGasFeeApisDisabled) {\n return await getEstimatesUsingLegacyEndpoint(request);\n }\n\n throw new Error('Main gas fee/price estimation failed. Use fallback');\n } catch {\n return await getEstimatesUsingProvider(request);\n }\n}\n\n/**\n * Retrieve gas fee estimates using the EIP-1559 endpoint of the gas API.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingFeeMarketEndpoint(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n fetchGasEstimates,\n fetchGasEstimatesUrl,\n clientId,\n calculateTimeEstimate,\n } = request;\n\n const estimates = await fetchGasEstimates(fetchGasEstimatesUrl, clientId);\n\n const { suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas } =\n estimates.medium;\n\n const estimatedGasFeeTimeBounds = calculateTimeEstimate(\n suggestedMaxPriorityFeePerGas,\n suggestedMaxFeePerGas,\n estimates,\n );\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds,\n gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,\n };\n}\n\n/**\n * Retrieve gas fee estimates using the legacy endpoint of the gas API.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingLegacyEndpoint(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n fetchLegacyGasPriceEstimates,\n fetchLegacyGasPriceEstimatesUrl,\n clientId,\n } = request;\n\n const estimates = await fetchLegacyGasPriceEstimates(\n fetchLegacyGasPriceEstimatesUrl,\n clientId,\n );\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY,\n };\n}\n\n/**\n * Retrieve gas fee estimates using an `eth_gasPrice` call to the RPC provider.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingProvider(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const { ethQuery, fetchEthGasPriceEstimate } = request;\n\n const estimates = await fetchEthGasPriceEstimate(ethQuery);\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE,\n };\n}\n"],"mappings":"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","names":["fetchGasEstimates","calculateTimeEstimate","fetchLegacyGasPriceEstimates","fetchEthGasPriceEstimate"]} -\ No newline at end of file -diff --git a/dist/chunk-BEVZS3YV.mjs b/dist/chunk-BEVZS3YV.mjs -deleted file mode 100644 -index 3be5db0695ee6a2c71e64ed76c43a1f30cc9e5ac..0000000000000000000000000000000000000000 ---- a/dist/chunk-BEVZS3YV.mjs -+++ /dev/null -@@ -1,396 +0,0 @@ --import { -- __privateAdd, -- __privateGet, -- __privateMethod, -- __privateSet, -- calculateTimeEstimate, -- fetchEthGasPriceEstimate, -- fetchGasEstimates, -- fetchLegacyGasPriceEstimates --} from "./chunk-KORLXV32.mjs"; -- --// src/GasFeeController.ts --import { -- convertHexToDecimal, -- safelyExecute, -- toHex --} from "@metamask/controller-utils"; --import EthQuery from "@metamask/eth-query"; --import { StaticIntervalPollingController } from "@metamask/polling-controller"; --import { v1 as random } from "uuid"; --var GAS_API_BASE_URL = "https://gas.api.infura.io"; --var GAS_ESTIMATE_TYPES = { -- FEE_MARKET: "fee-market", -- LEGACY: "legacy", -- ETH_GASPRICE: "eth_gasPrice", -- NONE: "none" --}; --var metadata = { -- gasFeeEstimatesByChainId: { -- persist: true, -- anonymous: false -- }, -- gasFeeEstimates: { persist: true, anonymous: false }, -- estimatedGasFeeTimeBounds: { persist: true, anonymous: false }, -- gasEstimateType: { persist: true, anonymous: false }, -- nonRPCGasFeeApisDisabled: { persist: true, anonymous: false } --}; --var name = "GasFeeController"; --var defaultState = { -- gasFeeEstimatesByChainId: {}, -- gasFeeEstimates: {}, -- estimatedGasFeeTimeBounds: {}, -- gasEstimateType: GAS_ESTIMATE_TYPES.NONE, -- nonRPCGasFeeApisDisabled: false --}; --var _getProvider, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn; --var GasFeeController = class extends StaticIntervalPollingController { -- /** -- * Creates a GasFeeController instance. -- * -- * @param options - The controller options. -- * @param options.interval - The time in milliseconds to wait between polls. -- * @param options.messenger - The controller messenger. -- * @param options.state - The initial state. -- * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current -- * network is EIP-1559 compatible. -- * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the -- * current network is compatible with the legacy gas price API. -- * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current -- * account is EIP-1559 compatible. -- * @param options.getChainId - Returns the current chain ID. -- * @param options.getProvider - Returns a network provider for the current network. -- * @param options.onNetworkDidChange - A function for registering an event handler for the -- * network state change event. -- * @param options.clientId - The client ID used to identify to the gas estimation API who is -- * asking for estimates. -- * @param options.infuraAPIKey - The Infura API key used for infura API requests. -- */ -- constructor({ -- interval = 15e3, -- messenger, -- state, -- getCurrentNetworkEIP1559Compatibility, -- getCurrentAccountEIP1559Compatibility, -- getChainId, -- getCurrentNetworkLegacyGasAPICompatibility, -- getProvider, -- onNetworkDidChange, -- clientId, -- infuraAPIKey -- }) { -- super({ -- name, -- metadata, -- messenger, -- state: { ...defaultState, ...state } -- }); -- __privateAdd(this, _onNetworkControllerDidChange); -- __privateAdd(this, _getProvider, void 0); -- this.intervalDelay = interval; -- this.setIntervalLength(interval); -- this.pollTokens = /* @__PURE__ */ new Set(); -- this.getCurrentNetworkEIP1559Compatibility = getCurrentNetworkEIP1559Compatibility; -- this.getCurrentNetworkLegacyGasAPICompatibility = getCurrentNetworkLegacyGasAPICompatibility; -- this.getCurrentAccountEIP1559Compatibility = getCurrentAccountEIP1559Compatibility; -- __privateSet(this, _getProvider, getProvider); -- this.EIP1559APIEndpoint = `${GAS_API_BASE_URL}/networks//suggestedGasFees`; -- this.legacyAPIEndpoint = `${GAS_API_BASE_URL}/networks//gasPrices`; -- this.clientId = clientId; -- this.infuraAPIKey = infuraAPIKey; -- this.ethQuery = new EthQuery(__privateGet(this, _getProvider).call(this)); -- if (onNetworkDidChange && getChainId) { -- this.currentChainId = getChainId(); -- onNetworkDidChange(async (networkControllerState) => { -- await __privateMethod(this, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn).call(this, networkControllerState); -- }); -- } else { -- this.currentChainId = this.messagingSystem.call( -- "NetworkController:getState" -- ).providerConfig.chainId; -- this.messagingSystem.subscribe( -- "NetworkController:networkDidChange", -- async (networkControllerState) => { -- await __privateMethod(this, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn).call(this, networkControllerState); -- } -- ); -- } -- } -- async resetPolling() { -- if (this.pollTokens.size !== 0) { -- const tokens = Array.from(this.pollTokens); -- this.stopPolling(); -- await this.getGasFeeEstimatesAndStartPolling(tokens[0]); -- tokens.slice(1).forEach((token) => { -- this.pollTokens.add(token); -- }); -- } -- } -- async fetchGasFeeEstimates(options) { -- return await this._fetchGasFeeEstimateData(options); -- } -- async getGasFeeEstimatesAndStartPolling(pollToken) { -- const _pollToken = pollToken || random(); -- this.pollTokens.add(_pollToken); -- if (this.pollTokens.size === 1) { -- await this._fetchGasFeeEstimateData(); -- this._poll(); -- } -- return _pollToken; -- } -- /** -- * Gets and sets gasFeeEstimates in state. -- * -- * @param options - The gas fee estimate options. -- * @param options.shouldUpdateState - Determines whether the state should be updated with the -- * updated gas estimates. -- * @returns The gas fee estimates. -- */ -- async _fetchGasFeeEstimateData(options = {}) { -- const { shouldUpdateState = true, networkClientId } = options; -- let ethQuery, isEIP1559Compatible, isLegacyGasAPICompatible, decimalChainId; -- if (networkClientId !== void 0) { -- const networkClient = this.messagingSystem.call( -- "NetworkController:getNetworkClientById", -- networkClientId -- ); -- isLegacyGasAPICompatible = networkClient.configuration.chainId === "0x38"; -- decimalChainId = convertHexToDecimal(networkClient.configuration.chainId); -- try { -- const result = await this.messagingSystem.call( -- "NetworkController:getEIP1559Compatibility", -- networkClientId -- ); -- isEIP1559Compatible = result || false; -- } catch { -- isEIP1559Compatible = false; -- } -- ethQuery = new EthQuery(networkClient.provider); -- } -- ethQuery ?? (ethQuery = this.ethQuery); -- isLegacyGasAPICompatible ?? (isLegacyGasAPICompatible = this.getCurrentNetworkLegacyGasAPICompatibility()); -- decimalChainId ?? (decimalChainId = convertHexToDecimal(this.currentChainId)); -- try { -- isEIP1559Compatible ?? (isEIP1559Compatible = await this.getEIP1559Compatibility()); -- } catch (e) { -- console.error(e); -- isEIP1559Compatible ?? (isEIP1559Compatible = false); -- } -- const gasFeeCalculations = await determineGasFeeCalculations({ -- isEIP1559Compatible, -- isLegacyGasAPICompatible, -- fetchGasEstimates, -- fetchGasEstimatesUrl: this.EIP1559APIEndpoint.replace( -- "", -- `${decimalChainId}` -- ), -- fetchLegacyGasPriceEstimates, -- fetchLegacyGasPriceEstimatesUrl: this.legacyAPIEndpoint.replace( -- "", -- `${decimalChainId}` -- ), -- fetchEthGasPriceEstimate, -- calculateTimeEstimate, -- clientId: this.clientId, -- ethQuery, -- infuraAPIKey: this.infuraAPIKey, -- nonRPCGasFeeApisDisabled: this.state.nonRPCGasFeeApisDisabled -- }); -- if (shouldUpdateState) { -- const chainId = toHex(decimalChainId); -- this.update((state) => { -- if (this.currentChainId === chainId) { -- state.gasFeeEstimates = gasFeeCalculations.gasFeeEstimates; -- state.estimatedGasFeeTimeBounds = gasFeeCalculations.estimatedGasFeeTimeBounds; -- state.gasEstimateType = gasFeeCalculations.gasEstimateType; -- } -- state.gasFeeEstimatesByChainId ?? (state.gasFeeEstimatesByChainId = {}); -- state.gasFeeEstimatesByChainId[chainId] = { -- gasFeeEstimates: gasFeeCalculations.gasFeeEstimates, -- estimatedGasFeeTimeBounds: gasFeeCalculations.estimatedGasFeeTimeBounds, -- gasEstimateType: gasFeeCalculations.gasEstimateType -- }; -- }); -- } -- return gasFeeCalculations; -- } -- /** -- * Remove the poll token, and stop polling if the set of poll tokens is empty. -- * -- * @param pollToken - The poll token to disconnect. -- */ -- disconnectPoller(pollToken) { -- this.pollTokens.delete(pollToken); -- if (this.pollTokens.size === 0) { -- this.stopPolling(); -- } -- } -- stopPolling() { -- if (this.intervalId) { -- clearInterval(this.intervalId); -- } -- this.pollTokens.clear(); -- this.resetState(); -- } -- /** -- * Prepare to discard this controller. -- * -- * This stops any active polling. -- */ -- destroy() { -- super.destroy(); -- this.stopPolling(); -- } -- _poll() { -- if (this.intervalId) { -- clearInterval(this.intervalId); -- } -- this.intervalId = setInterval(async () => { -- await safelyExecute(() => this._fetchGasFeeEstimateData()); -- }, this.intervalDelay); -- } -- /** -- * Fetching token list from the Token Service API. -- * -- * @private -- * @param networkClientId - The ID of the network client triggering the fetch. -- * @returns A promise that resolves when this operation completes. -- */ -- async _executePoll(networkClientId) { -- await this._fetchGasFeeEstimateData({ networkClientId }); -- } -- resetState() { -- this.update(() => { -- return defaultState; -- }); -- } -- async getEIP1559Compatibility() { -- const currentNetworkIsEIP1559Compatible = await this.getCurrentNetworkEIP1559Compatibility(); -- const currentAccountIsEIP1559Compatible = this.getCurrentAccountEIP1559Compatibility?.() ?? true; -- return currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible; -- } -- getTimeEstimate(maxPriorityFeePerGas, maxFeePerGas) { -- if (!this.state.gasFeeEstimates || this.state.gasEstimateType !== GAS_ESTIMATE_TYPES.FEE_MARKET) { -- return {}; -- } -- return calculateTimeEstimate( -- maxPriorityFeePerGas, -- maxFeePerGas, -- this.state.gasFeeEstimates -- ); -- } -- enableNonRPCGasFeeApis() { -- this.update((state) => { -- state.nonRPCGasFeeApisDisabled = false; -- }); -- } -- disableNonRPCGasFeeApis() { -- this.update((state) => { -- state.nonRPCGasFeeApisDisabled = true; -- }); -- } --}; --_getProvider = new WeakMap(); --_onNetworkControllerDidChange = new WeakSet(); --onNetworkControllerDidChange_fn = async function(networkControllerState) { -- const newChainId = networkControllerState.providerConfig.chainId; -- if (newChainId !== this.currentChainId) { -- this.ethQuery = new EthQuery(__privateGet(this, _getProvider).call(this)); -- await this.resetPolling(); -- this.currentChainId = newChainId; -- } --}; --var GasFeeController_default = GasFeeController; -- --// src/determineGasFeeCalculations.ts --async function determineGasFeeCalculations(args) { -- try { -- return await getEstimatesUsingFallbacks(args); -- } catch (error) { -- if (error instanceof Error) { -- throw new Error( -- `Gas fee/price estimation failed. Message: ${error.message}` -- ); -- } -- throw error; -- } --} --async function getEstimatesUsingFallbacks(request) { -- const { -- isEIP1559Compatible, -- isLegacyGasAPICompatible, -- nonRPCGasFeeApisDisabled -- } = request; -- try { -- if (isEIP1559Compatible && !nonRPCGasFeeApisDisabled) { -- return await getEstimatesUsingFeeMarketEndpoint(request); -- } -- if (isLegacyGasAPICompatible && !nonRPCGasFeeApisDisabled) { -- return await getEstimatesUsingLegacyEndpoint(request); -- } -- throw new Error("Main gas fee/price estimation failed. Use fallback"); -- } catch { -- return await getEstimatesUsingProvider(request); -- } --} --async function getEstimatesUsingFeeMarketEndpoint(request) { -- const { -- fetchGasEstimates: fetchGasEstimates2, -- fetchGasEstimatesUrl, -- infuraAPIKey, -- clientId, -- calculateTimeEstimate: calculateTimeEstimate2 -- } = request; -- const estimates = await fetchGasEstimates2( -- fetchGasEstimatesUrl, -- infuraAPIKey, -- clientId -- ); -- const { suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas } = estimates.medium; -- const estimatedGasFeeTimeBounds = calculateTimeEstimate2( -- suggestedMaxPriorityFeePerGas, -- suggestedMaxFeePerGas, -- estimates -- ); -- return { -- gasFeeEstimates: estimates, -- estimatedGasFeeTimeBounds, -- gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET -- }; --} --async function getEstimatesUsingLegacyEndpoint(request) { -- const { -- fetchLegacyGasPriceEstimates: fetchLegacyGasPriceEstimates2, -- fetchLegacyGasPriceEstimatesUrl, -- infuraAPIKey, -- clientId -- } = request; -- const estimates = await fetchLegacyGasPriceEstimates2( -- fetchLegacyGasPriceEstimatesUrl, -- infuraAPIKey, -- clientId -- ); -- return { -- gasFeeEstimates: estimates, -- estimatedGasFeeTimeBounds: {}, -- gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY -- }; --} --async function getEstimatesUsingProvider(request) { -- const { ethQuery, fetchEthGasPriceEstimate: fetchEthGasPriceEstimate2 } = request; -- const estimates = await fetchEthGasPriceEstimate2(ethQuery); -- return { -- gasFeeEstimates: estimates, -- estimatedGasFeeTimeBounds: {}, -- gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE -- }; --} -- --export { -- determineGasFeeCalculations, -- GAS_API_BASE_URL, -- GAS_ESTIMATE_TYPES, -- GasFeeController, -- GasFeeController_default --}; --//# sourceMappingURL=chunk-BEVZS3YV.mjs.map -\ No newline at end of file -diff --git a/dist/chunk-BEVZS3YV.mjs.map b/dist/chunk-BEVZS3YV.mjs.map -deleted file mode 100644 -index fc90025f10e73e5cdafc8964cd84365e51ad0c42..0000000000000000000000000000000000000000 ---- a/dist/chunk-BEVZS3YV.mjs.map -+++ /dev/null -@@ -1 +0,0 @@ --{"version":3,"sources":["../src/GasFeeController.ts","../src/determineGasFeeCalculations.ts"],"sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport {\n convertHexToDecimal,\n safelyExecute,\n toHex,\n} from '@metamask/controller-utils';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClientId,\n NetworkControllerGetEIP1559CompatibilityAction,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerNetworkDidChangeEvent,\n NetworkState,\n ProviderProxy,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { Hex } from '@metamask/utils';\nimport { v1 as random } from 'uuid';\n\nimport determineGasFeeCalculations from './determineGasFeeCalculations';\nimport {\n calculateTimeEstimate,\n fetchGasEstimates,\n fetchLegacyGasPriceEstimates,\n fetchEthGasPriceEstimate,\n} from './gas-util';\n\nexport const GAS_API_BASE_URL = 'https://gas.api.infura.io';\n\nexport type unknownString = 'unknown';\n\n// Fee Market describes the way gas is set after the london hardfork, and was\n// defined by EIP-1559.\nexport type FeeMarketEstimateType = 'fee-market';\n// Legacy describes gasPrice estimates from before london hardfork, when the\n// user is connected to mainnet and are presented with fast/average/slow\n// estimate levels to choose from.\nexport type LegacyEstimateType = 'legacy';\n// EthGasPrice describes a gasPrice estimate received from eth_gasPrice. Post\n// london this value should only be used for legacy type transactions when on\n// networks that support EIP-1559. This type of estimate is the most accurate\n// to display on custom networks that don't support EIP-1559.\nexport type EthGasPriceEstimateType = 'eth_gasPrice';\n// NoEstimate describes the state of the controller before receiving its first\n// estimate.\nexport type NoEstimateType = 'none';\n\n/**\n * Indicates which type of gasEstimate the controller is currently returning.\n * This is useful as a way of asserting that the shape of gasEstimates matches\n * expectations. NONE is a special case indicating that no previous gasEstimate\n * has been fetched.\n */\nexport const GAS_ESTIMATE_TYPES = {\n FEE_MARKET: 'fee-market' as FeeMarketEstimateType,\n LEGACY: 'legacy' as LegacyEstimateType,\n ETH_GASPRICE: 'eth_gasPrice' as EthGasPriceEstimateType,\n NONE: 'none' as NoEstimateType,\n};\n\nexport type GasEstimateType =\n | FeeMarketEstimateType\n | EthGasPriceEstimateType\n | LegacyEstimateType\n | NoEstimateType;\n\nexport type EstimatedGasFeeTimeBounds = {\n lowerTimeBound: number | null;\n upperTimeBound: number | unknownString;\n};\n\n/**\n * @type EthGasPriceEstimate\n *\n * A single gas price estimate for networks and accounts that don't support EIP-1559\n * This estimate comes from eth_gasPrice but is converted to dec gwei to match other\n * return values\n * @property gasPrice - A GWEI dec string\n */\n\nexport type EthGasPriceEstimate = {\n gasPrice: string;\n};\n\n/**\n * @type LegacyGasPriceEstimate\n *\n * A set of gas price estimates for networks and accounts that don't support EIP-1559\n * These estimates include low, medium and high all as strings representing gwei in\n * decimal format.\n * @property high - gasPrice, in decimal gwei string format, suggested for fast inclusion\n * @property medium - gasPrice, in decimal gwei string format, suggested for avg inclusion\n * @property low - gasPrice, in decimal gwei string format, suggested for slow inclusion\n */\nexport type LegacyGasPriceEstimate = {\n high: string;\n medium: string;\n low: string;\n};\n\n/**\n * @type Eip1559GasFee\n *\n * Data necessary to provide an estimate of a gas fee with a specific tip\n * @property minWaitTimeEstimate - The fastest the transaction will take, in milliseconds\n * @property maxWaitTimeEstimate - The slowest the transaction will take, in milliseconds\n * @property suggestedMaxPriorityFeePerGas - A suggested \"tip\", a GWEI hex number\n * @property suggestedMaxFeePerGas - A suggested max fee, the most a user will pay. a GWEI hex number\n */\nexport type Eip1559GasFee = {\n minWaitTimeEstimate: number; // a time duration in milliseconds\n maxWaitTimeEstimate: number; // a time duration in milliseconds\n suggestedMaxPriorityFeePerGas: string; // a GWEI decimal number\n suggestedMaxFeePerGas: string; // a GWEI decimal number\n};\n\n/**\n * @type GasFeeEstimates\n *\n * Data necessary to provide multiple GasFee estimates, and supporting information, to the user\n * @property low - A GasFee for a minimum necessary combination of tip and maxFee\n * @property medium - A GasFee for a recommended combination of tip and maxFee\n * @property high - A GasFee for a high combination of tip and maxFee\n * @property estimatedBaseFee - An estimate of what the base fee will be for the pending/next block. A GWEI dec number\n * @property networkCongestion - A normalized number that can be used to gauge the congestion\n * level of the network, with 0 meaning not congested and 1 meaning extremely congested\n */\nexport type GasFeeEstimates = SourcedGasFeeEstimates | FallbackGasFeeEstimates;\n\ntype SourcedGasFeeEstimates = {\n low: Eip1559GasFee;\n medium: Eip1559GasFee;\n high: Eip1559GasFee;\n estimatedBaseFee: string;\n historicalBaseFeeRange: [string, string];\n baseFeeTrend: 'up' | 'down' | 'level';\n latestPriorityFeeRange: [string, string];\n historicalPriorityFeeRange: [string, string];\n priorityFeeTrend: 'up' | 'down' | 'level';\n networkCongestion: number;\n};\n\ntype FallbackGasFeeEstimates = {\n low: Eip1559GasFee;\n medium: Eip1559GasFee;\n high: Eip1559GasFee;\n estimatedBaseFee: string;\n historicalBaseFeeRange: null;\n baseFeeTrend: null;\n latestPriorityFeeRange: null;\n historicalPriorityFeeRange: null;\n priorityFeeTrend: null;\n networkCongestion: null;\n};\n\nconst metadata = {\n gasFeeEstimatesByChainId: {\n persist: true,\n anonymous: false,\n },\n gasFeeEstimates: { persist: true, anonymous: false },\n estimatedGasFeeTimeBounds: { persist: true, anonymous: false },\n gasEstimateType: { persist: true, anonymous: false },\n nonRPCGasFeeApisDisabled: { persist: true, anonymous: false },\n};\n\nexport type GasFeeStateEthGasPrice = {\n gasFeeEstimates: EthGasPriceEstimate;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: EthGasPriceEstimateType;\n};\n\nexport type GasFeeStateFeeMarket = {\n gasFeeEstimates: GasFeeEstimates;\n estimatedGasFeeTimeBounds: EstimatedGasFeeTimeBounds | Record;\n gasEstimateType: FeeMarketEstimateType;\n};\n\nexport type GasFeeStateLegacy = {\n gasFeeEstimates: LegacyGasPriceEstimate;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: LegacyEstimateType;\n};\n\nexport type GasFeeStateNoEstimates = {\n gasFeeEstimates: Record;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: NoEstimateType;\n};\n\nexport type FetchGasFeeEstimateOptions = {\n shouldUpdateState?: boolean;\n networkClientId?: NetworkClientId;\n};\n\n/**\n * @type GasFeeState\n *\n * Gas Fee controller state\n * @property gasFeeEstimates - Gas fee estimate data based on new EIP-1559 properties\n * @property estimatedGasFeeTimeBounds - Estimates representing the minimum and maximum\n */\nexport type SingleChainGasFeeState =\n | GasFeeStateEthGasPrice\n | GasFeeStateFeeMarket\n | GasFeeStateLegacy\n | GasFeeStateNoEstimates;\n\nexport type GasFeeEstimatesByChainId = {\n gasFeeEstimatesByChainId?: Record;\n};\n\nexport type GasFeeState = GasFeeEstimatesByChainId &\n SingleChainGasFeeState & {\n nonRPCGasFeeApisDisabled?: boolean;\n };\n\nconst name = 'GasFeeController';\n\nexport type GasFeeStateChange = ControllerStateChangeEvent<\n typeof name,\n GasFeeState\n>;\n\nexport type GetGasFeeState = ControllerGetStateAction;\n\nexport type GasFeeControllerActions = GetGasFeeState;\n\nexport type GasFeeControllerEvents = GasFeeStateChange;\n\ntype AllowedActions =\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetEIP1559CompatibilityAction;\n\ntype GasFeeMessenger = RestrictedControllerMessenger<\n typeof name,\n GasFeeControllerActions | AllowedActions,\n GasFeeControllerEvents | NetworkControllerNetworkDidChangeEvent,\n AllowedActions['type'],\n NetworkControllerNetworkDidChangeEvent['type']\n>;\n\nconst defaultState: GasFeeState = {\n gasFeeEstimatesByChainId: {},\n gasFeeEstimates: {},\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.NONE,\n nonRPCGasFeeApisDisabled: false,\n};\n\n/**\n * Controller that retrieves gas fee estimate data and polls for updated data on a set interval\n */\nexport class GasFeeController extends StaticIntervalPollingController<\n typeof name,\n GasFeeState,\n GasFeeMessenger\n> {\n private intervalId?: ReturnType;\n\n private readonly intervalDelay;\n\n private readonly pollTokens: Set;\n\n private readonly legacyAPIEndpoint: string;\n\n private readonly EIP1559APIEndpoint: string;\n\n private readonly getCurrentNetworkEIP1559Compatibility;\n\n private readonly getCurrentNetworkLegacyGasAPICompatibility;\n\n private readonly getCurrentAccountEIP1559Compatibility;\n\n private readonly infuraAPIKey: string;\n\n private currentChainId;\n\n private ethQuery?: EthQuery;\n\n private readonly clientId?: string;\n\n #getProvider: () => ProviderProxy;\n\n /**\n * Creates a GasFeeController instance.\n *\n * @param options - The controller options.\n * @param options.interval - The time in milliseconds to wait between polls.\n * @param options.messenger - The controller messenger.\n * @param options.state - The initial state.\n * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current\n * network is EIP-1559 compatible.\n * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the\n * current network is compatible with the legacy gas price API.\n * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current\n * account is EIP-1559 compatible.\n * @param options.getChainId - Returns the current chain ID.\n * @param options.getProvider - Returns a network provider for the current network.\n * @param options.onNetworkDidChange - A function for registering an event handler for the\n * network state change event.\n * @param options.clientId - The client ID used to identify to the gas estimation API who is\n * asking for estimates.\n * @param options.infuraAPIKey - The Infura API key used for infura API requests.\n */\n constructor({\n interval = 15000,\n messenger,\n state,\n getCurrentNetworkEIP1559Compatibility,\n getCurrentAccountEIP1559Compatibility,\n getChainId,\n getCurrentNetworkLegacyGasAPICompatibility,\n getProvider,\n onNetworkDidChange,\n clientId,\n infuraAPIKey,\n }: {\n interval?: number;\n messenger: GasFeeMessenger;\n state?: GasFeeState;\n getCurrentNetworkEIP1559Compatibility: () => Promise;\n getCurrentNetworkLegacyGasAPICompatibility: () => boolean;\n getCurrentAccountEIP1559Compatibility?: () => boolean;\n getChainId?: () => Hex;\n getProvider: () => ProviderProxy;\n onNetworkDidChange?: (listener: (state: NetworkState) => void) => void;\n clientId?: string;\n infuraAPIKey: string;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.intervalDelay = interval;\n this.setIntervalLength(interval);\n this.pollTokens = new Set();\n this.getCurrentNetworkEIP1559Compatibility =\n getCurrentNetworkEIP1559Compatibility;\n this.getCurrentNetworkLegacyGasAPICompatibility =\n getCurrentNetworkLegacyGasAPICompatibility;\n this.getCurrentAccountEIP1559Compatibility =\n getCurrentAccountEIP1559Compatibility;\n this.#getProvider = getProvider;\n this.EIP1559APIEndpoint = `${GAS_API_BASE_URL}/networks//suggestedGasFees`;\n this.legacyAPIEndpoint = `${GAS_API_BASE_URL}/networks//gasPrices`;\n this.clientId = clientId;\n this.infuraAPIKey = infuraAPIKey;\n\n this.ethQuery = new EthQuery(this.#getProvider());\n\n if (onNetworkDidChange && getChainId) {\n this.currentChainId = getChainId();\n onNetworkDidChange(async (networkControllerState) => {\n await this.#onNetworkControllerDidChange(networkControllerState);\n });\n } else {\n this.currentChainId = this.messagingSystem.call(\n 'NetworkController:getState',\n ).providerConfig.chainId;\n this.messagingSystem.subscribe(\n 'NetworkController:networkDidChange',\n async (networkControllerState) => {\n await this.#onNetworkControllerDidChange(networkControllerState);\n },\n );\n }\n }\n\n async resetPolling() {\n if (this.pollTokens.size !== 0) {\n const tokens = Array.from(this.pollTokens);\n this.stopPolling();\n await this.getGasFeeEstimatesAndStartPolling(tokens[0]);\n tokens.slice(1).forEach((token) => {\n this.pollTokens.add(token);\n });\n }\n }\n\n async fetchGasFeeEstimates(options?: FetchGasFeeEstimateOptions) {\n return await this._fetchGasFeeEstimateData(options);\n }\n\n async getGasFeeEstimatesAndStartPolling(\n pollToken: string | undefined,\n ): Promise {\n const _pollToken = pollToken || random();\n\n this.pollTokens.add(_pollToken);\n\n if (this.pollTokens.size === 1) {\n await this._fetchGasFeeEstimateData();\n this._poll();\n }\n\n return _pollToken;\n }\n\n /**\n * Gets and sets gasFeeEstimates in state.\n *\n * @param options - The gas fee estimate options.\n * @param options.shouldUpdateState - Determines whether the state should be updated with the\n * updated gas estimates.\n * @returns The gas fee estimates.\n */\n async _fetchGasFeeEstimateData(\n options: FetchGasFeeEstimateOptions = {},\n ): Promise {\n const { shouldUpdateState = true, networkClientId } = options;\n\n let ethQuery,\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n decimalChainId: number;\n\n if (networkClientId !== undefined) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n isLegacyGasAPICompatible = networkClient.configuration.chainId === '0x38';\n\n decimalChainId = convertHexToDecimal(networkClient.configuration.chainId);\n\n try {\n const result = await this.messagingSystem.call(\n 'NetworkController:getEIP1559Compatibility',\n networkClientId,\n );\n isEIP1559Compatible = result || false;\n } catch {\n isEIP1559Compatible = false;\n }\n ethQuery = new EthQuery(networkClient.provider);\n }\n\n ethQuery ??= this.ethQuery;\n\n isLegacyGasAPICompatible ??=\n this.getCurrentNetworkLegacyGasAPICompatibility();\n\n decimalChainId ??= convertHexToDecimal(this.currentChainId);\n\n try {\n isEIP1559Compatible ??= await this.getEIP1559Compatibility();\n } catch (e) {\n console.error(e);\n isEIP1559Compatible ??= false;\n }\n\n const gasFeeCalculations = await determineGasFeeCalculations({\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n fetchGasEstimates,\n fetchGasEstimatesUrl: this.EIP1559APIEndpoint.replace(\n '',\n `${decimalChainId}`,\n ),\n fetchLegacyGasPriceEstimates,\n fetchLegacyGasPriceEstimatesUrl: this.legacyAPIEndpoint.replace(\n '',\n `${decimalChainId}`,\n ),\n fetchEthGasPriceEstimate,\n calculateTimeEstimate,\n clientId: this.clientId,\n ethQuery,\n infuraAPIKey: this.infuraAPIKey,\n nonRPCGasFeeApisDisabled: this.state.nonRPCGasFeeApisDisabled,\n });\n\n if (shouldUpdateState) {\n const chainId = toHex(decimalChainId);\n this.update((state) => {\n if (this.currentChainId === chainId) {\n state.gasFeeEstimates = gasFeeCalculations.gasFeeEstimates;\n state.estimatedGasFeeTimeBounds =\n gasFeeCalculations.estimatedGasFeeTimeBounds;\n state.gasEstimateType = gasFeeCalculations.gasEstimateType;\n }\n state.gasFeeEstimatesByChainId ??= {};\n state.gasFeeEstimatesByChainId[chainId] = {\n gasFeeEstimates: gasFeeCalculations.gasFeeEstimates,\n estimatedGasFeeTimeBounds:\n gasFeeCalculations.estimatedGasFeeTimeBounds,\n gasEstimateType: gasFeeCalculations.gasEstimateType,\n } as SingleChainGasFeeState;\n });\n }\n\n return gasFeeCalculations;\n }\n\n /**\n * Remove the poll token, and stop polling if the set of poll tokens is empty.\n *\n * @param pollToken - The poll token to disconnect.\n */\n disconnectPoller(pollToken: string) {\n this.pollTokens.delete(pollToken);\n if (this.pollTokens.size === 0) {\n this.stopPolling();\n }\n }\n\n stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n this.pollTokens.clear();\n this.resetState();\n }\n\n /**\n * Prepare to discard this controller.\n *\n * This stops any active polling.\n */\n override destroy() {\n super.destroy();\n this.stopPolling();\n }\n\n private _poll() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n\n this.intervalId = setInterval(async () => {\n await safelyExecute(() => this._fetchGasFeeEstimateData());\n }, this.intervalDelay);\n }\n\n /**\n * Fetching token list from the Token Service API.\n *\n * @private\n * @param networkClientId - The ID of the network client triggering the fetch.\n * @returns A promise that resolves when this operation completes.\n */\n async _executePoll(networkClientId: string): Promise {\n await this._fetchGasFeeEstimateData({ networkClientId });\n }\n\n private resetState() {\n this.update(() => {\n return defaultState;\n });\n }\n\n private async getEIP1559Compatibility() {\n const currentNetworkIsEIP1559Compatible =\n await this.getCurrentNetworkEIP1559Compatibility();\n const currentAccountIsEIP1559Compatible =\n this.getCurrentAccountEIP1559Compatibility?.() ?? true;\n\n return (\n currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible\n );\n }\n\n getTimeEstimate(\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n ): EstimatedGasFeeTimeBounds | Record {\n if (\n !this.state.gasFeeEstimates ||\n this.state.gasEstimateType !== GAS_ESTIMATE_TYPES.FEE_MARKET\n ) {\n return {};\n }\n return calculateTimeEstimate(\n maxPriorityFeePerGas,\n maxFeePerGas,\n this.state.gasFeeEstimates,\n );\n }\n\n async #onNetworkControllerDidChange(networkControllerState: NetworkState) {\n const newChainId = networkControllerState.providerConfig.chainId;\n\n if (newChainId !== this.currentChainId) {\n this.ethQuery = new EthQuery(this.#getProvider());\n await this.resetPolling();\n\n this.currentChainId = newChainId;\n }\n }\n\n enableNonRPCGasFeeApis() {\n this.update((state) => {\n state.nonRPCGasFeeApisDisabled = false;\n });\n }\n\n disableNonRPCGasFeeApis() {\n this.update((state) => {\n state.nonRPCGasFeeApisDisabled = true;\n });\n }\n}\n\nexport default GasFeeController;\n","import type {\n EstimatedGasFeeTimeBounds,\n EthGasPriceEstimate,\n GasFeeEstimates,\n GasFeeState as GasFeeCalculations,\n LegacyGasPriceEstimate,\n} from './GasFeeController';\nimport { GAS_ESTIMATE_TYPES } from './GasFeeController';\n\ntype DetermineGasFeeCalculationsRequest = {\n isEIP1559Compatible: boolean;\n isLegacyGasAPICompatible: boolean;\n fetchGasEstimates: (\n url: string,\n infuraAPIKey: string,\n clientId?: string,\n ) => Promise;\n fetchGasEstimatesUrl: string;\n fetchLegacyGasPriceEstimates: (\n url: string,\n infuraAPIKey: string,\n clientId?: string,\n ) => Promise;\n fetchLegacyGasPriceEstimatesUrl: string;\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n fetchEthGasPriceEstimate: (ethQuery: any) => Promise;\n calculateTimeEstimate: (\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n gasFeeEstimates: GasFeeEstimates,\n ) => EstimatedGasFeeTimeBounds;\n clientId: string | undefined;\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ethQuery: any;\n infuraAPIKey: string;\n nonRPCGasFeeApisDisabled?: boolean;\n};\n\n/**\n * Obtains a set of max base and priority fee estimates along with time estimates so that we\n * can present them to users when they are sending transactions or making swaps.\n *\n * @param args - The arguments.\n * @param args.isEIP1559Compatible - Governs whether or not we can use an EIP-1559-only method to\n * produce estimates.\n * @param args.isLegacyGasAPICompatible - Governs whether or not we can use a non-EIP-1559 method to\n * produce estimates (for instance, testnets do not support estimates altogether).\n * @param args.fetchGasEstimates - A function that fetches gas estimates using an EIP-1559-specific\n * API.\n * @param args.fetchGasEstimatesUrl - The URL for the API we can use to obtain EIP-1559-specific\n * estimates.\n * @param args.fetchLegacyGasPriceEstimates - A function that fetches gas estimates using an\n * non-EIP-1559-specific API.\n * @param args.fetchLegacyGasPriceEstimatesUrl - The URL for the API we can use to obtain\n * non-EIP-1559-specific estimates.\n * @param args.fetchEthGasPriceEstimate - A function that fetches gas estimates using\n * `eth_gasPrice`.\n * @param args.calculateTimeEstimate - A function that determine time estimate bounds.\n * @param args.clientId - An identifier that an API can use to know who is asking for estimates.\n * @param args.ethQuery - An EthQuery instance we can use to talk to Ethereum directly.\n * @param args.infuraAPIKey - Infura API key to use for requests to Infura.\n * @param args.nonRPCGasFeeApisDisabled - Whether to disable requests to the legacyAPIEndpoint and the EIP1559APIEndpoint\n * @returns The gas fee calculations.\n */\nexport default async function determineGasFeeCalculations(\n args: DetermineGasFeeCalculationsRequest,\n): Promise {\n try {\n return await getEstimatesUsingFallbacks(args);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(\n `Gas fee/price estimation failed. Message: ${error.message}`,\n );\n }\n\n throw error;\n }\n}\n\n/**\n * Retrieve the gas fee estimates using a series of fallback mechanisms.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingFallbacks(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n nonRPCGasFeeApisDisabled,\n } = request;\n\n try {\n if (isEIP1559Compatible && !nonRPCGasFeeApisDisabled) {\n return await getEstimatesUsingFeeMarketEndpoint(request);\n }\n\n if (isLegacyGasAPICompatible && !nonRPCGasFeeApisDisabled) {\n return await getEstimatesUsingLegacyEndpoint(request);\n }\n\n throw new Error('Main gas fee/price estimation failed. Use fallback');\n } catch {\n return await getEstimatesUsingProvider(request);\n }\n}\n\n/**\n * Retrieve gas fee estimates using the EIP-1559 endpoint of the gas API.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingFeeMarketEndpoint(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n fetchGasEstimates,\n fetchGasEstimatesUrl,\n infuraAPIKey,\n clientId,\n calculateTimeEstimate,\n } = request;\n\n const estimates = await fetchGasEstimates(\n fetchGasEstimatesUrl,\n infuraAPIKey,\n clientId,\n );\n\n const { suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas } =\n estimates.medium;\n\n const estimatedGasFeeTimeBounds = calculateTimeEstimate(\n suggestedMaxPriorityFeePerGas,\n suggestedMaxFeePerGas,\n estimates,\n );\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds,\n gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,\n };\n}\n\n/**\n * Retrieve gas fee estimates using the legacy endpoint of the gas API.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingLegacyEndpoint(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n fetchLegacyGasPriceEstimates,\n fetchLegacyGasPriceEstimatesUrl,\n infuraAPIKey,\n clientId,\n } = request;\n\n const estimates = await fetchLegacyGasPriceEstimates(\n fetchLegacyGasPriceEstimatesUrl,\n infuraAPIKey,\n clientId,\n );\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY,\n };\n}\n\n/**\n * Retrieve gas fee estimates using an `eth_gasPrice` call to the RPC provider.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingProvider(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const { ethQuery, fetchEthGasPriceEstimate } = request;\n\n const estimates = await fetchEthGasPriceEstimate(ethQuery);\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE,\n };\n}\n"],"mappings":";;;;;;;;;;;;AAKA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,OAAO,cAAc;AAUrB,SAAS,uCAAuC;AAEhD,SAAS,MAAM,cAAc;AAUtB,IAAM,mBAAmB;AA0BzB,IAAM,qBAAqB;AAAA,EAChC,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,MAAM;AACR;AAiGA,IAAM,WAAW;AAAA,EACf,0BAA0B;AAAA,IACxB,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AAAA,EACA,iBAAiB,EAAE,SAAS,MAAM,WAAW,MAAM;AAAA,EACnD,2BAA2B,EAAE,SAAS,MAAM,WAAW,MAAM;AAAA,EAC7D,iBAAiB,EAAE,SAAS,MAAM,WAAW,MAAM;AAAA,EACnD,0BAA0B,EAAE,SAAS,MAAM,WAAW,MAAM;AAC9D;AAqDA,IAAM,OAAO;AA0Bb,IAAM,eAA4B;AAAA,EAChC,0BAA0B,CAAC;AAAA,EAC3B,iBAAiB,CAAC;AAAA,EAClB,2BAA2B,CAAC;AAAA,EAC5B,iBAAiB,mBAAmB;AAAA,EACpC,0BAA0B;AAC5B;AA9PA;AAmQO,IAAM,mBAAN,cAA+B,gCAIpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgDA,YAAY;AAAA,IACV,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAYG;AACD,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,EAAE,GAAG,cAAc,GAAG,MAAM;AAAA,IACrC,CAAC;AAuPH,uBAAM;AA5SN;AAsDE,SAAK,gBAAgB;AACrB,SAAK,kBAAkB,QAAQ;AAC/B,SAAK,aAAa,oBAAI,IAAI;AAC1B,SAAK,wCACH;AACF,SAAK,6CACH;AACF,SAAK,wCACH;AACF,uBAAK,cAAe;AACpB,SAAK,qBAAqB,GAAG,gBAAgB;AAC7C,SAAK,oBAAoB,GAAG,gBAAgB;AAC5C,SAAK,WAAW;AAChB,SAAK,eAAe;AAEpB,SAAK,WAAW,IAAI,SAAS,mBAAK,cAAL,UAAmB;AAEhD,QAAI,sBAAsB,YAAY;AACpC,WAAK,iBAAiB,WAAW;AACjC,yBAAmB,OAAO,2BAA2B;AACnD,cAAM,sBAAK,gEAAL,WAAmC;AAAA,MAC3C,CAAC;AAAA,IACH,OAAO;AACL,WAAK,iBAAiB,KAAK,gBAAgB;AAAA,QACzC;AAAA,MACF,EAAE,eAAe;AACjB,WAAK,gBAAgB;AAAA,QACnB;AAAA,QACA,OAAO,2BAA2B;AAChC,gBAAM,sBAAK,gEAAL,WAAmC;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,eAAe;AACnB,QAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,YAAM,SAAS,MAAM,KAAK,KAAK,UAAU;AACzC,WAAK,YAAY;AACjB,YAAM,KAAK,kCAAkC,OAAO,CAAC,CAAC;AACtD,aAAO,MAAM,CAAC,EAAE,QAAQ,CAAC,UAAU;AACjC,aAAK,WAAW,IAAI,KAAK;AAAA,MAC3B,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,qBAAqB,SAAsC;AAC/D,WAAO,MAAM,KAAK,yBAAyB,OAAO;AAAA,EACpD;AAAA,EAEA,MAAM,kCACJ,WACiB;AACjB,UAAM,aAAa,aAAa,OAAO;AAEvC,SAAK,WAAW,IAAI,UAAU;AAE9B,QAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,YAAM,KAAK,yBAAyB;AACpC,WAAK,MAAM;AAAA,IACb;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,yBACJ,UAAsC,CAAC,GACjB;AACtB,UAAM,EAAE,oBAAoB,MAAM,gBAAgB,IAAI;AAEtD,QAAI,UACF,qBACA,0BACA;AAEF,QAAI,oBAAoB,QAAW;AACjC,YAAM,gBAAgB,KAAK,gBAAgB;AAAA,QACzC;AAAA,QACA;AAAA,MACF;AACA,iCAA2B,cAAc,cAAc,YAAY;AAEnE,uBAAiB,oBAAoB,cAAc,cAAc,OAAO;AAExE,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,gBAAgB;AAAA,UACxC;AAAA,UACA;AAAA,QACF;AACA,8BAAsB,UAAU;AAAA,MAClC,QAAQ;AACN,8BAAsB;AAAA,MACxB;AACA,iBAAW,IAAI,SAAS,cAAc,QAAQ;AAAA,IAChD;AAEA,4BAAa,KAAK;AAElB,4DACE,KAAK,2CAA2C;AAElD,wCAAmB,oBAAoB,KAAK,cAAc;AAE1D,QAAI;AACF,oDAAwB,MAAM,KAAK,wBAAwB;AAAA,IAC7D,SAAS,GAAG;AACV,cAAQ,MAAM,CAAC;AACf,oDAAwB;AAAA,IAC1B;AAEA,UAAM,qBAAqB,MAAM,4BAA4B;AAAA,MAC3D;AAAA,MACA;AAAA,MACA;AAAA,MACA,sBAAsB,KAAK,mBAAmB;AAAA,QAC5C;AAAA,QACA,GAAG,cAAc;AAAA,MACnB;AAAA,MACA;AAAA,MACA,iCAAiC,KAAK,kBAAkB;AAAA,QACtD;AAAA,QACA,GAAG,cAAc;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,KAAK;AAAA,MACf;AAAA,MACA,cAAc,KAAK;AAAA,MACnB,0BAA0B,KAAK,MAAM;AAAA,IACvC,CAAC;AAED,QAAI,mBAAmB;AACrB,YAAM,UAAU,MAAM,cAAc;AACpC,WAAK,OAAO,CAAC,UAAU;AACrB,YAAI,KAAK,mBAAmB,SAAS;AACnC,gBAAM,kBAAkB,mBAAmB;AAC3C,gBAAM,4BACJ,mBAAmB;AACrB,gBAAM,kBAAkB,mBAAmB;AAAA,QAC7C;AACA,cAAM,6BAAN,MAAM,2BAA6B,CAAC;AACpC,cAAM,yBAAyB,OAAO,IAAI;AAAA,UACxC,iBAAiB,mBAAmB;AAAA,UACpC,2BACE,mBAAmB;AAAA,UACrB,iBAAiB,mBAAmB;AAAA,QACtC;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiB,WAAmB;AAClC,SAAK,WAAW,OAAO,SAAS;AAChC,QAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,cAAc;AACZ,QAAI,KAAK,YAAY;AACnB,oBAAc,KAAK,UAAU;AAAA,IAC/B;AACA,SAAK,WAAW,MAAM;AACtB,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOS,UAAU;AACjB,UAAM,QAAQ;AACd,SAAK,YAAY;AAAA,EACnB;AAAA,EAEQ,QAAQ;AACd,QAAI,KAAK,YAAY;AACnB,oBAAc,KAAK,UAAU;AAAA,IAC/B;AAEA,SAAK,aAAa,YAAY,YAAY;AACxC,YAAM,cAAc,MAAM,KAAK,yBAAyB,CAAC;AAAA,IAC3D,GAAG,KAAK,aAAa;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,aAAa,iBAAwC;AACzD,UAAM,KAAK,yBAAyB,EAAE,gBAAgB,CAAC;AAAA,EACzD;AAAA,EAEQ,aAAa;AACnB,SAAK,OAAO,MAAM;AAChB,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,0BAA0B;AACtC,UAAM,oCACJ,MAAM,KAAK,sCAAsC;AACnD,UAAM,oCACJ,KAAK,wCAAwC,KAAK;AAEpD,WACE,qCAAqC;AAAA,EAEzC;AAAA,EAEA,gBACE,sBACA,cACmD;AACnD,QACE,CAAC,KAAK,MAAM,mBACZ,KAAK,MAAM,oBAAoB,mBAAmB,YAClD;AACA,aAAO,CAAC;AAAA,IACV;AACA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,KAAK,MAAM;AAAA,IACb;AAAA,EACF;AAAA,EAaA,yBAAyB;AACvB,SAAK,OAAO,CAAC,UAAU;AACrB,YAAM,2BAA2B;AAAA,IACnC,CAAC;AAAA,EACH;AAAA,EAEA,0BAA0B;AACxB,SAAK,OAAO,CAAC,UAAU;AACrB,YAAM,2BAA2B;AAAA,IACnC,CAAC;AAAA,EACH;AACF;AAlUE;AA4SM;AAAA,kCAA6B,eAAC,wBAAsC;AACxE,QAAM,aAAa,uBAAuB,eAAe;AAEzD,MAAI,eAAe,KAAK,gBAAgB;AACtC,SAAK,WAAW,IAAI,SAAS,mBAAK,cAAL,UAAmB;AAChD,UAAM,KAAK,aAAa;AAExB,SAAK,iBAAiB;AAAA,EACxB;AACF;AAeF,IAAO,2BAAQ;;;ACliBf,eAAO,4BACL,MAC6B;AAC7B,MAAI;AACF,WAAO,MAAM,2BAA2B,IAAI;AAAA,EAC9C,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO;AAC1B,YAAM,IAAI;AAAA,QACR,6CAA6C,MAAM,OAAO;AAAA,MAC5D;AAAA,IACF;AAEA,UAAM;AAAA,EACR;AACF;AAOA,eAAe,2BACb,SAC6B;AAC7B,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,MAAI;AACF,QAAI,uBAAuB,CAAC,0BAA0B;AACpD,aAAO,MAAM,mCAAmC,OAAO;AAAA,IACzD;AAEA,QAAI,4BAA4B,CAAC,0BAA0B;AACzD,aAAO,MAAM,gCAAgC,OAAO;AAAA,IACtD;AAEA,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE,QAAQ;AACN,WAAO,MAAM,0BAA0B,OAAO;AAAA,EAChD;AACF;AAOA,eAAe,mCACb,SAC6B;AAC7B,QAAM;AAAA,IACJ,mBAAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,uBAAAC;AAAA,EACF,IAAI;AAEJ,QAAM,YAAY,MAAMD;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,EAAE,+BAA+B,sBAAsB,IAC3D,UAAU;AAEZ,QAAM,4BAA4BC;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AAAA,IACL,iBAAiB;AAAA,IACjB;AAAA,IACA,iBAAiB,mBAAmB;AAAA,EACtC;AACF;AAOA,eAAe,gCACb,SAC6B;AAC7B,QAAM;AAAA,IACJ,8BAAAC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,YAAY,MAAMA;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,2BAA2B,CAAC;AAAA,IAC5B,iBAAiB,mBAAmB;AAAA,EACtC;AACF;AAOA,eAAe,0BACb,SAC6B;AAC7B,QAAM,EAAE,UAAU,0BAAAC,0BAAyB,IAAI;AAE/C,QAAM,YAAY,MAAMA,0BAAyB,QAAQ;AAEzD,SAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,2BAA2B,CAAC;AAAA,IAC5B,iBAAiB,mBAAmB;AAAA,EACtC;AACF;","names":["fetchGasEstimates","calculateTimeEstimate","fetchLegacyGasPriceEstimates","fetchEthGasPriceEstimate"]} -\ No newline at end of file -diff --git a/dist/chunk-H5WHAYLI.js b/dist/chunk-H5WHAYLI.js -deleted file mode 100644 -index 3d6f8458707153d0b3cdd19da2bce7cb32da8eef..0000000000000000000000000000000000000000 ---- a/dist/chunk-H5WHAYLI.js -+++ /dev/null -@@ -1,396 +0,0 @@ --"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -- -- -- -- -- -- -- -- --var _chunkQ2YPK5SLjs = require('./chunk-Q2YPK5SL.js'); -- --// src/GasFeeController.ts -- -- -- -- --var _controllerutils = require('@metamask/controller-utils'); --var _ethquery = require('@metamask/eth-query'); var _ethquery2 = _interopRequireDefault(_ethquery); --var _pollingcontroller = require('@metamask/polling-controller'); --var _uuid = require('uuid'); --var GAS_API_BASE_URL = "https://gas.api.infura.io"; --var GAS_ESTIMATE_TYPES = { -- FEE_MARKET: "fee-market", -- LEGACY: "legacy", -- ETH_GASPRICE: "eth_gasPrice", -- NONE: "none" --}; --var metadata = { -- gasFeeEstimatesByChainId: { -- persist: true, -- anonymous: false -- }, -- gasFeeEstimates: { persist: true, anonymous: false }, -- estimatedGasFeeTimeBounds: { persist: true, anonymous: false }, -- gasEstimateType: { persist: true, anonymous: false }, -- nonRPCGasFeeApisDisabled: { persist: true, anonymous: false } --}; --var name = "GasFeeController"; --var defaultState = { -- gasFeeEstimatesByChainId: {}, -- gasFeeEstimates: {}, -- estimatedGasFeeTimeBounds: {}, -- gasEstimateType: GAS_ESTIMATE_TYPES.NONE, -- nonRPCGasFeeApisDisabled: false --}; --var _getProvider, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn; --var GasFeeController = class extends _pollingcontroller.StaticIntervalPollingController { -- /** -- * Creates a GasFeeController instance. -- * -- * @param options - The controller options. -- * @param options.interval - The time in milliseconds to wait between polls. -- * @param options.messenger - The controller messenger. -- * @param options.state - The initial state. -- * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current -- * network is EIP-1559 compatible. -- * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the -- * current network is compatible with the legacy gas price API. -- * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current -- * account is EIP-1559 compatible. -- * @param options.getChainId - Returns the current chain ID. -- * @param options.getProvider - Returns a network provider for the current network. -- * @param options.onNetworkDidChange - A function for registering an event handler for the -- * network state change event. -- * @param options.clientId - The client ID used to identify to the gas estimation API who is -- * asking for estimates. -- * @param options.infuraAPIKey - The Infura API key used for infura API requests. -- */ -- constructor({ -- interval = 15e3, -- messenger, -- state, -- getCurrentNetworkEIP1559Compatibility, -- getCurrentAccountEIP1559Compatibility, -- getChainId, -- getCurrentNetworkLegacyGasAPICompatibility, -- getProvider, -- onNetworkDidChange, -- clientId, -- infuraAPIKey -- }) { -- super({ -- name, -- metadata, -- messenger, -- state: { ...defaultState, ...state } -- }); -- _chunkQ2YPK5SLjs.__privateAdd.call(void 0, this, _onNetworkControllerDidChange); -- _chunkQ2YPK5SLjs.__privateAdd.call(void 0, this, _getProvider, void 0); -- this.intervalDelay = interval; -- this.setIntervalLength(interval); -- this.pollTokens = /* @__PURE__ */ new Set(); -- this.getCurrentNetworkEIP1559Compatibility = getCurrentNetworkEIP1559Compatibility; -- this.getCurrentNetworkLegacyGasAPICompatibility = getCurrentNetworkLegacyGasAPICompatibility; -- this.getCurrentAccountEIP1559Compatibility = getCurrentAccountEIP1559Compatibility; -- _chunkQ2YPK5SLjs.__privateSet.call(void 0, this, _getProvider, getProvider); -- this.EIP1559APIEndpoint = `${GAS_API_BASE_URL}/networks//suggestedGasFees`; -- this.legacyAPIEndpoint = `${GAS_API_BASE_URL}/networks//gasPrices`; -- this.clientId = clientId; -- this.infuraAPIKey = infuraAPIKey; -- this.ethQuery = new (0, _ethquery2.default)(_chunkQ2YPK5SLjs.__privateGet.call(void 0, this, _getProvider).call(this)); -- if (onNetworkDidChange && getChainId) { -- this.currentChainId = getChainId(); -- onNetworkDidChange(async (networkControllerState) => { -- await _chunkQ2YPK5SLjs.__privateMethod.call(void 0, this, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn).call(this, networkControllerState); -- }); -- } else { -- this.currentChainId = this.messagingSystem.call( -- "NetworkController:getState" -- ).providerConfig.chainId; -- this.messagingSystem.subscribe( -- "NetworkController:networkDidChange", -- async (networkControllerState) => { -- await _chunkQ2YPK5SLjs.__privateMethod.call(void 0, this, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn).call(this, networkControllerState); -- } -- ); -- } -- } -- async resetPolling() { -- if (this.pollTokens.size !== 0) { -- const tokens = Array.from(this.pollTokens); -- this.stopPolling(); -- await this.getGasFeeEstimatesAndStartPolling(tokens[0]); -- tokens.slice(1).forEach((token) => { -- this.pollTokens.add(token); -- }); -- } -- } -- async fetchGasFeeEstimates(options) { -- return await this._fetchGasFeeEstimateData(options); -- } -- async getGasFeeEstimatesAndStartPolling(pollToken) { -- const _pollToken = pollToken || _uuid.v1.call(void 0, ); -- this.pollTokens.add(_pollToken); -- if (this.pollTokens.size === 1) { -- await this._fetchGasFeeEstimateData(); -- this._poll(); -- } -- return _pollToken; -- } -- /** -- * Gets and sets gasFeeEstimates in state. -- * -- * @param options - The gas fee estimate options. -- * @param options.shouldUpdateState - Determines whether the state should be updated with the -- * updated gas estimates. -- * @returns The gas fee estimates. -- */ -- async _fetchGasFeeEstimateData(options = {}) { -- const { shouldUpdateState = true, networkClientId } = options; -- let ethQuery, isEIP1559Compatible, isLegacyGasAPICompatible, decimalChainId; -- if (networkClientId !== void 0) { -- const networkClient = this.messagingSystem.call( -- "NetworkController:getNetworkClientById", -- networkClientId -- ); -- isLegacyGasAPICompatible = networkClient.configuration.chainId === "0x38"; -- decimalChainId = _controllerutils.convertHexToDecimal.call(void 0, networkClient.configuration.chainId); -- try { -- const result = await this.messagingSystem.call( -- "NetworkController:getEIP1559Compatibility", -- networkClientId -- ); -- isEIP1559Compatible = result || false; -- } catch { -- isEIP1559Compatible = false; -- } -- ethQuery = new (0, _ethquery2.default)(networkClient.provider); -- } -- ethQuery ?? (ethQuery = this.ethQuery); -- isLegacyGasAPICompatible ?? (isLegacyGasAPICompatible = this.getCurrentNetworkLegacyGasAPICompatibility()); -- decimalChainId ?? (decimalChainId = _controllerutils.convertHexToDecimal.call(void 0, this.currentChainId)); -- try { -- isEIP1559Compatible ?? (isEIP1559Compatible = await this.getEIP1559Compatibility()); -- } catch (e) { -- console.error(e); -- isEIP1559Compatible ?? (isEIP1559Compatible = false); -- } -- const gasFeeCalculations = await determineGasFeeCalculations({ -- isEIP1559Compatible, -- isLegacyGasAPICompatible, -- fetchGasEstimates: _chunkQ2YPK5SLjs.fetchGasEstimates, -- fetchGasEstimatesUrl: this.EIP1559APIEndpoint.replace( -- "", -- `${decimalChainId}` -- ), -- fetchLegacyGasPriceEstimates: _chunkQ2YPK5SLjs.fetchLegacyGasPriceEstimates, -- fetchLegacyGasPriceEstimatesUrl: this.legacyAPIEndpoint.replace( -- "", -- `${decimalChainId}` -- ), -- fetchEthGasPriceEstimate: _chunkQ2YPK5SLjs.fetchEthGasPriceEstimate, -- calculateTimeEstimate: _chunkQ2YPK5SLjs.calculateTimeEstimate, -- clientId: this.clientId, -- ethQuery, -- infuraAPIKey: this.infuraAPIKey, -- nonRPCGasFeeApisDisabled: this.state.nonRPCGasFeeApisDisabled -- }); -- if (shouldUpdateState) { -- const chainId = _controllerutils.toHex.call(void 0, decimalChainId); -- this.update((state) => { -- if (this.currentChainId === chainId) { -- state.gasFeeEstimates = gasFeeCalculations.gasFeeEstimates; -- state.estimatedGasFeeTimeBounds = gasFeeCalculations.estimatedGasFeeTimeBounds; -- state.gasEstimateType = gasFeeCalculations.gasEstimateType; -- } -- state.gasFeeEstimatesByChainId ?? (state.gasFeeEstimatesByChainId = {}); -- state.gasFeeEstimatesByChainId[chainId] = { -- gasFeeEstimates: gasFeeCalculations.gasFeeEstimates, -- estimatedGasFeeTimeBounds: gasFeeCalculations.estimatedGasFeeTimeBounds, -- gasEstimateType: gasFeeCalculations.gasEstimateType -- }; -- }); -- } -- return gasFeeCalculations; -- } -- /** -- * Remove the poll token, and stop polling if the set of poll tokens is empty. -- * -- * @param pollToken - The poll token to disconnect. -- */ -- disconnectPoller(pollToken) { -- this.pollTokens.delete(pollToken); -- if (this.pollTokens.size === 0) { -- this.stopPolling(); -- } -- } -- stopPolling() { -- if (this.intervalId) { -- clearInterval(this.intervalId); -- } -- this.pollTokens.clear(); -- this.resetState(); -- } -- /** -- * Prepare to discard this controller. -- * -- * This stops any active polling. -- */ -- destroy() { -- super.destroy(); -- this.stopPolling(); -- } -- _poll() { -- if (this.intervalId) { -- clearInterval(this.intervalId); -- } -- this.intervalId = setInterval(async () => { -- await _controllerutils.safelyExecute.call(void 0, () => this._fetchGasFeeEstimateData()); -- }, this.intervalDelay); -- } -- /** -- * Fetching token list from the Token Service API. -- * -- * @private -- * @param networkClientId - The ID of the network client triggering the fetch. -- * @returns A promise that resolves when this operation completes. -- */ -- async _executePoll(networkClientId) { -- await this._fetchGasFeeEstimateData({ networkClientId }); -- } -- resetState() { -- this.update(() => { -- return defaultState; -- }); -- } -- async getEIP1559Compatibility() { -- const currentNetworkIsEIP1559Compatible = await this.getCurrentNetworkEIP1559Compatibility(); -- const currentAccountIsEIP1559Compatible = this.getCurrentAccountEIP1559Compatibility?.() ?? true; -- return currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible; -- } -- getTimeEstimate(maxPriorityFeePerGas, maxFeePerGas) { -- if (!this.state.gasFeeEstimates || this.state.gasEstimateType !== GAS_ESTIMATE_TYPES.FEE_MARKET) { -- return {}; -- } -- return _chunkQ2YPK5SLjs.calculateTimeEstimate.call(void 0, -- maxPriorityFeePerGas, -- maxFeePerGas, -- this.state.gasFeeEstimates -- ); -- } -- enableNonRPCGasFeeApis() { -- this.update((state) => { -- state.nonRPCGasFeeApisDisabled = false; -- }); -- } -- disableNonRPCGasFeeApis() { -- this.update((state) => { -- state.nonRPCGasFeeApisDisabled = true; -- }); -- } --}; --_getProvider = new WeakMap(); --_onNetworkControllerDidChange = new WeakSet(); --onNetworkControllerDidChange_fn = async function(networkControllerState) { -- const newChainId = networkControllerState.providerConfig.chainId; -- if (newChainId !== this.currentChainId) { -- this.ethQuery = new (0, _ethquery2.default)(_chunkQ2YPK5SLjs.__privateGet.call(void 0, this, _getProvider).call(this)); -- await this.resetPolling(); -- this.currentChainId = newChainId; -- } --}; --var GasFeeController_default = GasFeeController; -- --// src/determineGasFeeCalculations.ts --async function determineGasFeeCalculations(args) { -- try { -- return await getEstimatesUsingFallbacks(args); -- } catch (error) { -- if (error instanceof Error) { -- throw new Error( -- `Gas fee/price estimation failed. Message: ${error.message}` -- ); -- } -- throw error; -- } --} --async function getEstimatesUsingFallbacks(request) { -- const { -- isEIP1559Compatible, -- isLegacyGasAPICompatible, -- nonRPCGasFeeApisDisabled -- } = request; -- try { -- if (isEIP1559Compatible && !nonRPCGasFeeApisDisabled) { -- return await getEstimatesUsingFeeMarketEndpoint(request); -- } -- if (isLegacyGasAPICompatible && !nonRPCGasFeeApisDisabled) { -- return await getEstimatesUsingLegacyEndpoint(request); -- } -- throw new Error("Main gas fee/price estimation failed. Use fallback"); -- } catch { -- return await getEstimatesUsingProvider(request); -- } --} --async function getEstimatesUsingFeeMarketEndpoint(request) { -- const { -- fetchGasEstimates: fetchGasEstimates2, -- fetchGasEstimatesUrl, -- infuraAPIKey, -- clientId, -- calculateTimeEstimate: calculateTimeEstimate2 -- } = request; -- const estimates = await fetchGasEstimates2( -- fetchGasEstimatesUrl, -- infuraAPIKey, -- clientId -- ); -- const { suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas } = estimates.medium; -- const estimatedGasFeeTimeBounds = calculateTimeEstimate2( -- suggestedMaxPriorityFeePerGas, -- suggestedMaxFeePerGas, -- estimates -- ); -- return { -- gasFeeEstimates: estimates, -- estimatedGasFeeTimeBounds, -- gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET -- }; --} --async function getEstimatesUsingLegacyEndpoint(request) { -- const { -- fetchLegacyGasPriceEstimates: fetchLegacyGasPriceEstimates2, -- fetchLegacyGasPriceEstimatesUrl, -- infuraAPIKey, -- clientId -- } = request; -- const estimates = await fetchLegacyGasPriceEstimates2( -- fetchLegacyGasPriceEstimatesUrl, -- infuraAPIKey, -- clientId -- ); -- return { -- gasFeeEstimates: estimates, -- estimatedGasFeeTimeBounds: {}, -- gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY -- }; --} --async function getEstimatesUsingProvider(request) { -- const { ethQuery, fetchEthGasPriceEstimate: fetchEthGasPriceEstimate2 } = request; -- const estimates = await fetchEthGasPriceEstimate2(ethQuery); -- return { -- gasFeeEstimates: estimates, -- estimatedGasFeeTimeBounds: {}, -- gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE -- }; --} -- -- -- -- -- -- -- --exports.determineGasFeeCalculations = determineGasFeeCalculations; exports.GAS_API_BASE_URL = GAS_API_BASE_URL; exports.GAS_ESTIMATE_TYPES = GAS_ESTIMATE_TYPES; exports.GasFeeController = GasFeeController; exports.GasFeeController_default = GasFeeController_default; --//# sourceMappingURL=chunk-H5WHAYLI.js.map -\ No newline at end of file -diff --git a/dist/chunk-H5WHAYLI.js.map b/dist/chunk-H5WHAYLI.js.map -deleted file mode 100644 -index ed761f584584470a8176f029d9d860dc017428fc..0000000000000000000000000000000000000000 ---- a/dist/chunk-H5WHAYLI.js.map -+++ /dev/null -@@ -1 +0,0 @@ --{"version":3,"sources":["../src/GasFeeController.ts","../src/determineGasFeeCalculations.ts"],"names":["fetchGasEstimates","calculateTimeEstimate","fetchLegacyGasPriceEstimates","fetchEthGasPriceEstimate"],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport {\n convertHexToDecimal,\n safelyExecute,\n toHex,\n} from '@metamask/controller-utils';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClientId,\n NetworkControllerGetEIP1559CompatibilityAction,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerNetworkDidChangeEvent,\n NetworkState,\n ProviderProxy,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { Hex } from '@metamask/utils';\nimport { v1 as random } from 'uuid';\n\nimport determineGasFeeCalculations from './determineGasFeeCalculations';\nimport {\n calculateTimeEstimate,\n fetchGasEstimates,\n fetchLegacyGasPriceEstimates,\n fetchEthGasPriceEstimate,\n} from './gas-util';\n\nexport const GAS_API_BASE_URL = 'https://gas.api.infura.io';\n\nexport type unknownString = 'unknown';\n\n// Fee Market describes the way gas is set after the london hardfork, and was\n// defined by EIP-1559.\nexport type FeeMarketEstimateType = 'fee-market';\n// Legacy describes gasPrice estimates from before london hardfork, when the\n// user is connected to mainnet and are presented with fast/average/slow\n// estimate levels to choose from.\nexport type LegacyEstimateType = 'legacy';\n// EthGasPrice describes a gasPrice estimate received from eth_gasPrice. Post\n// london this value should only be used for legacy type transactions when on\n// networks that support EIP-1559. This type of estimate is the most accurate\n// to display on custom networks that don't support EIP-1559.\nexport type EthGasPriceEstimateType = 'eth_gasPrice';\n// NoEstimate describes the state of the controller before receiving its first\n// estimate.\nexport type NoEstimateType = 'none';\n\n/**\n * Indicates which type of gasEstimate the controller is currently returning.\n * This is useful as a way of asserting that the shape of gasEstimates matches\n * expectations. NONE is a special case indicating that no previous gasEstimate\n * has been fetched.\n */\nexport const GAS_ESTIMATE_TYPES = {\n FEE_MARKET: 'fee-market' as FeeMarketEstimateType,\n LEGACY: 'legacy' as LegacyEstimateType,\n ETH_GASPRICE: 'eth_gasPrice' as EthGasPriceEstimateType,\n NONE: 'none' as NoEstimateType,\n};\n\nexport type GasEstimateType =\n | FeeMarketEstimateType\n | EthGasPriceEstimateType\n | LegacyEstimateType\n | NoEstimateType;\n\nexport type EstimatedGasFeeTimeBounds = {\n lowerTimeBound: number | null;\n upperTimeBound: number | unknownString;\n};\n\n/**\n * @type EthGasPriceEstimate\n *\n * A single gas price estimate for networks and accounts that don't support EIP-1559\n * This estimate comes from eth_gasPrice but is converted to dec gwei to match other\n * return values\n * @property gasPrice - A GWEI dec string\n */\n\nexport type EthGasPriceEstimate = {\n gasPrice: string;\n};\n\n/**\n * @type LegacyGasPriceEstimate\n *\n * A set of gas price estimates for networks and accounts that don't support EIP-1559\n * These estimates include low, medium and high all as strings representing gwei in\n * decimal format.\n * @property high - gasPrice, in decimal gwei string format, suggested for fast inclusion\n * @property medium - gasPrice, in decimal gwei string format, suggested for avg inclusion\n * @property low - gasPrice, in decimal gwei string format, suggested for slow inclusion\n */\nexport type LegacyGasPriceEstimate = {\n high: string;\n medium: string;\n low: string;\n};\n\n/**\n * @type Eip1559GasFee\n *\n * Data necessary to provide an estimate of a gas fee with a specific tip\n * @property minWaitTimeEstimate - The fastest the transaction will take, in milliseconds\n * @property maxWaitTimeEstimate - The slowest the transaction will take, in milliseconds\n * @property suggestedMaxPriorityFeePerGas - A suggested \"tip\", a GWEI hex number\n * @property suggestedMaxFeePerGas - A suggested max fee, the most a user will pay. a GWEI hex number\n */\nexport type Eip1559GasFee = {\n minWaitTimeEstimate: number; // a time duration in milliseconds\n maxWaitTimeEstimate: number; // a time duration in milliseconds\n suggestedMaxPriorityFeePerGas: string; // a GWEI decimal number\n suggestedMaxFeePerGas: string; // a GWEI decimal number\n};\n\n/**\n * @type GasFeeEstimates\n *\n * Data necessary to provide multiple GasFee estimates, and supporting information, to the user\n * @property low - A GasFee for a minimum necessary combination of tip and maxFee\n * @property medium - A GasFee for a recommended combination of tip and maxFee\n * @property high - A GasFee for a high combination of tip and maxFee\n * @property estimatedBaseFee - An estimate of what the base fee will be for the pending/next block. A GWEI dec number\n * @property networkCongestion - A normalized number that can be used to gauge the congestion\n * level of the network, with 0 meaning not congested and 1 meaning extremely congested\n */\nexport type GasFeeEstimates = SourcedGasFeeEstimates | FallbackGasFeeEstimates;\n\ntype SourcedGasFeeEstimates = {\n low: Eip1559GasFee;\n medium: Eip1559GasFee;\n high: Eip1559GasFee;\n estimatedBaseFee: string;\n historicalBaseFeeRange: [string, string];\n baseFeeTrend: 'up' | 'down' | 'level';\n latestPriorityFeeRange: [string, string];\n historicalPriorityFeeRange: [string, string];\n priorityFeeTrend: 'up' | 'down' | 'level';\n networkCongestion: number;\n};\n\ntype FallbackGasFeeEstimates = {\n low: Eip1559GasFee;\n medium: Eip1559GasFee;\n high: Eip1559GasFee;\n estimatedBaseFee: string;\n historicalBaseFeeRange: null;\n baseFeeTrend: null;\n latestPriorityFeeRange: null;\n historicalPriorityFeeRange: null;\n priorityFeeTrend: null;\n networkCongestion: null;\n};\n\nconst metadata = {\n gasFeeEstimatesByChainId: {\n persist: true,\n anonymous: false,\n },\n gasFeeEstimates: { persist: true, anonymous: false },\n estimatedGasFeeTimeBounds: { persist: true, anonymous: false },\n gasEstimateType: { persist: true, anonymous: false },\n nonRPCGasFeeApisDisabled: { persist: true, anonymous: false },\n};\n\nexport type GasFeeStateEthGasPrice = {\n gasFeeEstimates: EthGasPriceEstimate;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: EthGasPriceEstimateType;\n};\n\nexport type GasFeeStateFeeMarket = {\n gasFeeEstimates: GasFeeEstimates;\n estimatedGasFeeTimeBounds: EstimatedGasFeeTimeBounds | Record;\n gasEstimateType: FeeMarketEstimateType;\n};\n\nexport type GasFeeStateLegacy = {\n gasFeeEstimates: LegacyGasPriceEstimate;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: LegacyEstimateType;\n};\n\nexport type GasFeeStateNoEstimates = {\n gasFeeEstimates: Record;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: NoEstimateType;\n};\n\nexport type FetchGasFeeEstimateOptions = {\n shouldUpdateState?: boolean;\n networkClientId?: NetworkClientId;\n};\n\n/**\n * @type GasFeeState\n *\n * Gas Fee controller state\n * @property gasFeeEstimates - Gas fee estimate data based on new EIP-1559 properties\n * @property estimatedGasFeeTimeBounds - Estimates representing the minimum and maximum\n */\nexport type SingleChainGasFeeState =\n | GasFeeStateEthGasPrice\n | GasFeeStateFeeMarket\n | GasFeeStateLegacy\n | GasFeeStateNoEstimates;\n\nexport type GasFeeEstimatesByChainId = {\n gasFeeEstimatesByChainId?: Record;\n};\n\nexport type GasFeeState = GasFeeEstimatesByChainId &\n SingleChainGasFeeState & {\n nonRPCGasFeeApisDisabled?: boolean;\n };\n\nconst name = 'GasFeeController';\n\nexport type GasFeeStateChange = ControllerStateChangeEvent<\n typeof name,\n GasFeeState\n>;\n\nexport type GetGasFeeState = ControllerGetStateAction;\n\nexport type GasFeeControllerActions = GetGasFeeState;\n\nexport type GasFeeControllerEvents = GasFeeStateChange;\n\ntype AllowedActions =\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetEIP1559CompatibilityAction;\n\ntype GasFeeMessenger = RestrictedControllerMessenger<\n typeof name,\n GasFeeControllerActions | AllowedActions,\n GasFeeControllerEvents | NetworkControllerNetworkDidChangeEvent,\n AllowedActions['type'],\n NetworkControllerNetworkDidChangeEvent['type']\n>;\n\nconst defaultState: GasFeeState = {\n gasFeeEstimatesByChainId: {},\n gasFeeEstimates: {},\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.NONE,\n nonRPCGasFeeApisDisabled: false,\n};\n\n/**\n * Controller that retrieves gas fee estimate data and polls for updated data on a set interval\n */\nexport class GasFeeController extends StaticIntervalPollingController<\n typeof name,\n GasFeeState,\n GasFeeMessenger\n> {\n private intervalId?: ReturnType;\n\n private readonly intervalDelay;\n\n private readonly pollTokens: Set;\n\n private readonly legacyAPIEndpoint: string;\n\n private readonly EIP1559APIEndpoint: string;\n\n private readonly getCurrentNetworkEIP1559Compatibility;\n\n private readonly getCurrentNetworkLegacyGasAPICompatibility;\n\n private readonly getCurrentAccountEIP1559Compatibility;\n\n private readonly infuraAPIKey: string;\n\n private currentChainId;\n\n private ethQuery?: EthQuery;\n\n private readonly clientId?: string;\n\n #getProvider: () => ProviderProxy;\n\n /**\n * Creates a GasFeeController instance.\n *\n * @param options - The controller options.\n * @param options.interval - The time in milliseconds to wait between polls.\n * @param options.messenger - The controller messenger.\n * @param options.state - The initial state.\n * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current\n * network is EIP-1559 compatible.\n * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the\n * current network is compatible with the legacy gas price API.\n * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current\n * account is EIP-1559 compatible.\n * @param options.getChainId - Returns the current chain ID.\n * @param options.getProvider - Returns a network provider for the current network.\n * @param options.onNetworkDidChange - A function for registering an event handler for the\n * network state change event.\n * @param options.clientId - The client ID used to identify to the gas estimation API who is\n * asking for estimates.\n * @param options.infuraAPIKey - The Infura API key used for infura API requests.\n */\n constructor({\n interval = 15000,\n messenger,\n state,\n getCurrentNetworkEIP1559Compatibility,\n getCurrentAccountEIP1559Compatibility,\n getChainId,\n getCurrentNetworkLegacyGasAPICompatibility,\n getProvider,\n onNetworkDidChange,\n clientId,\n infuraAPIKey,\n }: {\n interval?: number;\n messenger: GasFeeMessenger;\n state?: GasFeeState;\n getCurrentNetworkEIP1559Compatibility: () => Promise;\n getCurrentNetworkLegacyGasAPICompatibility: () => boolean;\n getCurrentAccountEIP1559Compatibility?: () => boolean;\n getChainId?: () => Hex;\n getProvider: () => ProviderProxy;\n onNetworkDidChange?: (listener: (state: NetworkState) => void) => void;\n clientId?: string;\n infuraAPIKey: string;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.intervalDelay = interval;\n this.setIntervalLength(interval);\n this.pollTokens = new Set();\n this.getCurrentNetworkEIP1559Compatibility =\n getCurrentNetworkEIP1559Compatibility;\n this.getCurrentNetworkLegacyGasAPICompatibility =\n getCurrentNetworkLegacyGasAPICompatibility;\n this.getCurrentAccountEIP1559Compatibility =\n getCurrentAccountEIP1559Compatibility;\n this.#getProvider = getProvider;\n this.EIP1559APIEndpoint = `${GAS_API_BASE_URL}/networks//suggestedGasFees`;\n this.legacyAPIEndpoint = `${GAS_API_BASE_URL}/networks//gasPrices`;\n this.clientId = clientId;\n this.infuraAPIKey = infuraAPIKey;\n\n this.ethQuery = new EthQuery(this.#getProvider());\n\n if (onNetworkDidChange && getChainId) {\n this.currentChainId = getChainId();\n onNetworkDidChange(async (networkControllerState) => {\n await this.#onNetworkControllerDidChange(networkControllerState);\n });\n } else {\n this.currentChainId = this.messagingSystem.call(\n 'NetworkController:getState',\n ).providerConfig.chainId;\n this.messagingSystem.subscribe(\n 'NetworkController:networkDidChange',\n async (networkControllerState) => {\n await this.#onNetworkControllerDidChange(networkControllerState);\n },\n );\n }\n }\n\n async resetPolling() {\n if (this.pollTokens.size !== 0) {\n const tokens = Array.from(this.pollTokens);\n this.stopPolling();\n await this.getGasFeeEstimatesAndStartPolling(tokens[0]);\n tokens.slice(1).forEach((token) => {\n this.pollTokens.add(token);\n });\n }\n }\n\n async fetchGasFeeEstimates(options?: FetchGasFeeEstimateOptions) {\n return await this._fetchGasFeeEstimateData(options);\n }\n\n async getGasFeeEstimatesAndStartPolling(\n pollToken: string | undefined,\n ): Promise {\n const _pollToken = pollToken || random();\n\n this.pollTokens.add(_pollToken);\n\n if (this.pollTokens.size === 1) {\n await this._fetchGasFeeEstimateData();\n this._poll();\n }\n\n return _pollToken;\n }\n\n /**\n * Gets and sets gasFeeEstimates in state.\n *\n * @param options - The gas fee estimate options.\n * @param options.shouldUpdateState - Determines whether the state should be updated with the\n * updated gas estimates.\n * @returns The gas fee estimates.\n */\n async _fetchGasFeeEstimateData(\n options: FetchGasFeeEstimateOptions = {},\n ): Promise {\n const { shouldUpdateState = true, networkClientId } = options;\n\n let ethQuery,\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n decimalChainId: number;\n\n if (networkClientId !== undefined) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n isLegacyGasAPICompatible = networkClient.configuration.chainId === '0x38';\n\n decimalChainId = convertHexToDecimal(networkClient.configuration.chainId);\n\n try {\n const result = await this.messagingSystem.call(\n 'NetworkController:getEIP1559Compatibility',\n networkClientId,\n );\n isEIP1559Compatible = result || false;\n } catch {\n isEIP1559Compatible = false;\n }\n ethQuery = new EthQuery(networkClient.provider);\n }\n\n ethQuery ??= this.ethQuery;\n\n isLegacyGasAPICompatible ??=\n this.getCurrentNetworkLegacyGasAPICompatibility();\n\n decimalChainId ??= convertHexToDecimal(this.currentChainId);\n\n try {\n isEIP1559Compatible ??= await this.getEIP1559Compatibility();\n } catch (e) {\n console.error(e);\n isEIP1559Compatible ??= false;\n }\n\n const gasFeeCalculations = await determineGasFeeCalculations({\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n fetchGasEstimates,\n fetchGasEstimatesUrl: this.EIP1559APIEndpoint.replace(\n '',\n `${decimalChainId}`,\n ),\n fetchLegacyGasPriceEstimates,\n fetchLegacyGasPriceEstimatesUrl: this.legacyAPIEndpoint.replace(\n '',\n `${decimalChainId}`,\n ),\n fetchEthGasPriceEstimate,\n calculateTimeEstimate,\n clientId: this.clientId,\n ethQuery,\n infuraAPIKey: this.infuraAPIKey,\n nonRPCGasFeeApisDisabled: this.state.nonRPCGasFeeApisDisabled,\n });\n\n if (shouldUpdateState) {\n const chainId = toHex(decimalChainId);\n this.update((state) => {\n if (this.currentChainId === chainId) {\n state.gasFeeEstimates = gasFeeCalculations.gasFeeEstimates;\n state.estimatedGasFeeTimeBounds =\n gasFeeCalculations.estimatedGasFeeTimeBounds;\n state.gasEstimateType = gasFeeCalculations.gasEstimateType;\n }\n state.gasFeeEstimatesByChainId ??= {};\n state.gasFeeEstimatesByChainId[chainId] = {\n gasFeeEstimates: gasFeeCalculations.gasFeeEstimates,\n estimatedGasFeeTimeBounds:\n gasFeeCalculations.estimatedGasFeeTimeBounds,\n gasEstimateType: gasFeeCalculations.gasEstimateType,\n } as SingleChainGasFeeState;\n });\n }\n\n return gasFeeCalculations;\n }\n\n /**\n * Remove the poll token, and stop polling if the set of poll tokens is empty.\n *\n * @param pollToken - The poll token to disconnect.\n */\n disconnectPoller(pollToken: string) {\n this.pollTokens.delete(pollToken);\n if (this.pollTokens.size === 0) {\n this.stopPolling();\n }\n }\n\n stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n this.pollTokens.clear();\n this.resetState();\n }\n\n /**\n * Prepare to discard this controller.\n *\n * This stops any active polling.\n */\n override destroy() {\n super.destroy();\n this.stopPolling();\n }\n\n private _poll() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n\n this.intervalId = setInterval(async () => {\n await safelyExecute(() => this._fetchGasFeeEstimateData());\n }, this.intervalDelay);\n }\n\n /**\n * Fetching token list from the Token Service API.\n *\n * @private\n * @param networkClientId - The ID of the network client triggering the fetch.\n * @returns A promise that resolves when this operation completes.\n */\n async _executePoll(networkClientId: string): Promise {\n await this._fetchGasFeeEstimateData({ networkClientId });\n }\n\n private resetState() {\n this.update(() => {\n return defaultState;\n });\n }\n\n private async getEIP1559Compatibility() {\n const currentNetworkIsEIP1559Compatible =\n await this.getCurrentNetworkEIP1559Compatibility();\n const currentAccountIsEIP1559Compatible =\n this.getCurrentAccountEIP1559Compatibility?.() ?? true;\n\n return (\n currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible\n );\n }\n\n getTimeEstimate(\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n ): EstimatedGasFeeTimeBounds | Record {\n if (\n !this.state.gasFeeEstimates ||\n this.state.gasEstimateType !== GAS_ESTIMATE_TYPES.FEE_MARKET\n ) {\n return {};\n }\n return calculateTimeEstimate(\n maxPriorityFeePerGas,\n maxFeePerGas,\n this.state.gasFeeEstimates,\n );\n }\n\n async #onNetworkControllerDidChange(networkControllerState: NetworkState) {\n const newChainId = networkControllerState.providerConfig.chainId;\n\n if (newChainId !== this.currentChainId) {\n this.ethQuery = new EthQuery(this.#getProvider());\n await this.resetPolling();\n\n this.currentChainId = newChainId;\n }\n }\n\n enableNonRPCGasFeeApis() {\n this.update((state) => {\n state.nonRPCGasFeeApisDisabled = false;\n });\n }\n\n disableNonRPCGasFeeApis() {\n this.update((state) => {\n state.nonRPCGasFeeApisDisabled = true;\n });\n }\n}\n\nexport default GasFeeController;\n","import type {\n EstimatedGasFeeTimeBounds,\n EthGasPriceEstimate,\n GasFeeEstimates,\n GasFeeState as GasFeeCalculations,\n LegacyGasPriceEstimate,\n} from './GasFeeController';\nimport { GAS_ESTIMATE_TYPES } from './GasFeeController';\n\ntype DetermineGasFeeCalculationsRequest = {\n isEIP1559Compatible: boolean;\n isLegacyGasAPICompatible: boolean;\n fetchGasEstimates: (\n url: string,\n infuraAPIKey: string,\n clientId?: string,\n ) => Promise;\n fetchGasEstimatesUrl: string;\n fetchLegacyGasPriceEstimates: (\n url: string,\n infuraAPIKey: string,\n clientId?: string,\n ) => Promise;\n fetchLegacyGasPriceEstimatesUrl: string;\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n fetchEthGasPriceEstimate: (ethQuery: any) => Promise;\n calculateTimeEstimate: (\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n gasFeeEstimates: GasFeeEstimates,\n ) => EstimatedGasFeeTimeBounds;\n clientId: string | undefined;\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ethQuery: any;\n infuraAPIKey: string;\n nonRPCGasFeeApisDisabled?: boolean;\n};\n\n/**\n * Obtains a set of max base and priority fee estimates along with time estimates so that we\n * can present them to users when they are sending transactions or making swaps.\n *\n * @param args - The arguments.\n * @param args.isEIP1559Compatible - Governs whether or not we can use an EIP-1559-only method to\n * produce estimates.\n * @param args.isLegacyGasAPICompatible - Governs whether or not we can use a non-EIP-1559 method to\n * produce estimates (for instance, testnets do not support estimates altogether).\n * @param args.fetchGasEstimates - A function that fetches gas estimates using an EIP-1559-specific\n * API.\n * @param args.fetchGasEstimatesUrl - The URL for the API we can use to obtain EIP-1559-specific\n * estimates.\n * @param args.fetchLegacyGasPriceEstimates - A function that fetches gas estimates using an\n * non-EIP-1559-specific API.\n * @param args.fetchLegacyGasPriceEstimatesUrl - The URL for the API we can use to obtain\n * non-EIP-1559-specific estimates.\n * @param args.fetchEthGasPriceEstimate - A function that fetches gas estimates using\n * `eth_gasPrice`.\n * @param args.calculateTimeEstimate - A function that determine time estimate bounds.\n * @param args.clientId - An identifier that an API can use to know who is asking for estimates.\n * @param args.ethQuery - An EthQuery instance we can use to talk to Ethereum directly.\n * @param args.infuraAPIKey - Infura API key to use for requests to Infura.\n * @param args.nonRPCGasFeeApisDisabled - Whether to disable requests to the legacyAPIEndpoint and the EIP1559APIEndpoint\n * @returns The gas fee calculations.\n */\nexport default async function determineGasFeeCalculations(\n args: DetermineGasFeeCalculationsRequest,\n): Promise {\n try {\n return await getEstimatesUsingFallbacks(args);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(\n `Gas fee/price estimation failed. Message: ${error.message}`,\n );\n }\n\n throw error;\n }\n}\n\n/**\n * Retrieve the gas fee estimates using a series of fallback mechanisms.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingFallbacks(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n nonRPCGasFeeApisDisabled,\n } = request;\n\n try {\n if (isEIP1559Compatible && !nonRPCGasFeeApisDisabled) {\n return await getEstimatesUsingFeeMarketEndpoint(request);\n }\n\n if (isLegacyGasAPICompatible && !nonRPCGasFeeApisDisabled) {\n return await getEstimatesUsingLegacyEndpoint(request);\n }\n\n throw new Error('Main gas fee/price estimation failed. Use fallback');\n } catch {\n return await getEstimatesUsingProvider(request);\n }\n}\n\n/**\n * Retrieve gas fee estimates using the EIP-1559 endpoint of the gas API.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingFeeMarketEndpoint(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n fetchGasEstimates,\n fetchGasEstimatesUrl,\n infuraAPIKey,\n clientId,\n calculateTimeEstimate,\n } = request;\n\n const estimates = await fetchGasEstimates(\n fetchGasEstimatesUrl,\n infuraAPIKey,\n clientId,\n );\n\n const { suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas } =\n estimates.medium;\n\n const estimatedGasFeeTimeBounds = calculateTimeEstimate(\n suggestedMaxPriorityFeePerGas,\n suggestedMaxFeePerGas,\n estimates,\n );\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds,\n gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,\n };\n}\n\n/**\n * Retrieve gas fee estimates using the legacy endpoint of the gas API.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingLegacyEndpoint(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n fetchLegacyGasPriceEstimates,\n fetchLegacyGasPriceEstimatesUrl,\n infuraAPIKey,\n clientId,\n } = request;\n\n const estimates = await fetchLegacyGasPriceEstimates(\n fetchLegacyGasPriceEstimatesUrl,\n infuraAPIKey,\n clientId,\n );\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY,\n };\n}\n\n/**\n * Retrieve gas fee estimates using an `eth_gasPrice` call to the RPC provider.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingProvider(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const { ethQuery, fetchEthGasPriceEstimate } = request;\n\n const estimates = await fetchEthGasPriceEstimate(ethQuery);\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE,\n };\n}\n"]} -\ No newline at end of file -diff --git a/dist/chunk-KORLXV32.mjs b/dist/chunk-KORLXV32.mjs -deleted file mode 100644 -index a964582d5c161e6b650fe9447442ca9540f9fdb9..0000000000000000000000000000000000000000 ---- a/dist/chunk-KORLXV32.mjs -+++ /dev/null -@@ -1,165 +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/gas-util.ts --import { -- query, -- handleFetch, -- gweiDecToWEIBN, -- weiHexToGweiDec --} from "@metamask/controller-utils"; --import BN from "bn.js"; --var makeClientIdHeader = (clientId) => ({ "X-Client-Id": clientId }); --function normalizeGWEIDecimalNumbers(n) { -- const numberAsWEIHex = gweiDecToWEIBN(n).toString(16); -- const numberAsGWEI = weiHexToGweiDec(numberAsWEIHex); -- return numberAsGWEI; --} --async function fetchGasEstimates(url, infuraAPIKey, clientId) { -- const infuraAuthToken = buildInfuraAuthToken(infuraAPIKey); -- const estimates = await handleFetch(url, { -- headers: getHeaders(infuraAuthToken, clientId) -- }); -- return { -- low: { -- ...estimates.low, -- suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.low.suggestedMaxPriorityFeePerGas -- ), -- suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.low.suggestedMaxFeePerGas -- ) -- }, -- medium: { -- ...estimates.medium, -- suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.medium.suggestedMaxPriorityFeePerGas -- ), -- suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.medium.suggestedMaxFeePerGas -- ) -- }, -- high: { -- ...estimates.high, -- suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.high.suggestedMaxPriorityFeePerGas -- ), -- suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.high.suggestedMaxFeePerGas -- ) -- }, -- estimatedBaseFee: normalizeGWEIDecimalNumbers(estimates.estimatedBaseFee), -- historicalBaseFeeRange: estimates.historicalBaseFeeRange, -- baseFeeTrend: estimates.baseFeeTrend, -- latestPriorityFeeRange: estimates.latestPriorityFeeRange, -- historicalPriorityFeeRange: estimates.historicalPriorityFeeRange, -- priorityFeeTrend: estimates.priorityFeeTrend, -- networkCongestion: estimates.networkCongestion -- }; --} --async function fetchLegacyGasPriceEstimates(url, infuraAPIKey, clientId) { -- const infuraAuthToken = buildInfuraAuthToken(infuraAPIKey); -- const result = await handleFetch(url, { -- referrer: url, -- referrerPolicy: "no-referrer-when-downgrade", -- method: "GET", -- mode: "cors", -- headers: getHeaders(infuraAuthToken, clientId) -- }); -- return { -- low: result.SafeGasPrice, -- medium: result.ProposeGasPrice, -- high: result.FastGasPrice -- }; --} --async function fetchEthGasPriceEstimate(ethQuery) { -- const gasPrice = await query(ethQuery, "gasPrice"); -- return { -- gasPrice: weiHexToGweiDec(gasPrice).toString() -- }; --} --function calculateTimeEstimate(maxPriorityFeePerGas, maxFeePerGas, gasFeeEstimates) { -- const { low, medium, high, estimatedBaseFee } = gasFeeEstimates; -- const maxPriorityFeePerGasInWEI = gweiDecToWEIBN(maxPriorityFeePerGas); -- const maxFeePerGasInWEI = gweiDecToWEIBN(maxFeePerGas); -- const estimatedBaseFeeInWEI = gweiDecToWEIBN(estimatedBaseFee); -- const effectiveMaxPriorityFee = BN.min( -- maxPriorityFeePerGasInWEI, -- maxFeePerGasInWEI.sub(estimatedBaseFeeInWEI) -- ); -- const lowMaxPriorityFeeInWEI = gweiDecToWEIBN( -- low.suggestedMaxPriorityFeePerGas -- ); -- const mediumMaxPriorityFeeInWEI = gweiDecToWEIBN( -- medium.suggestedMaxPriorityFeePerGas -- ); -- const highMaxPriorityFeeInWEI = gweiDecToWEIBN( -- high.suggestedMaxPriorityFeePerGas -- ); -- let lowerTimeBound; -- let upperTimeBound; -- if (effectiveMaxPriorityFee.lt(lowMaxPriorityFeeInWEI)) { -- lowerTimeBound = null; -- upperTimeBound = "unknown"; -- } else if (effectiveMaxPriorityFee.gte(lowMaxPriorityFeeInWEI) && effectiveMaxPriorityFee.lt(mediumMaxPriorityFeeInWEI)) { -- lowerTimeBound = low.minWaitTimeEstimate; -- upperTimeBound = low.maxWaitTimeEstimate; -- } else if (effectiveMaxPriorityFee.gte(mediumMaxPriorityFeeInWEI) && effectiveMaxPriorityFee.lt(highMaxPriorityFeeInWEI)) { -- lowerTimeBound = medium.minWaitTimeEstimate; -- upperTimeBound = medium.maxWaitTimeEstimate; -- } else if (effectiveMaxPriorityFee.eq(highMaxPriorityFeeInWEI)) { -- lowerTimeBound = high.minWaitTimeEstimate; -- upperTimeBound = high.maxWaitTimeEstimate; -- } else { -- lowerTimeBound = 0; -- upperTimeBound = high.maxWaitTimeEstimate; -- } -- return { -- lowerTimeBound, -- upperTimeBound -- }; --} --function buildInfuraAuthToken(infuraAPIKey) { -- return Buffer.from(`${infuraAPIKey}:`).toString("base64"); --} --function getHeaders(infuraAuthToken, clientId) { -- return { -- "Content-Type": "application/json", -- Authorization: `Basic ${infuraAuthToken}`, -- // Only add the clientId header if clientId is a non-empty string -- ...clientId?.trim() ? makeClientIdHeader(clientId) : {} -- }; --} -- --export { -- __privateGet, -- __privateAdd, -- __privateSet, -- __privateMethod, -- normalizeGWEIDecimalNumbers, -- fetchGasEstimates, -- fetchLegacyGasPriceEstimates, -- fetchEthGasPriceEstimate, -- calculateTimeEstimate --}; --//# sourceMappingURL=chunk-KORLXV32.mjs.map -\ No newline at end of file -diff --git a/dist/chunk-KORLXV32.mjs.map b/dist/chunk-KORLXV32.mjs.map -deleted file mode 100644 -index b95130543a5b39392214c5fe873ad367e6fa8af7..0000000000000000000000000000000000000000 ---- a/dist/chunk-KORLXV32.mjs.map -+++ /dev/null -@@ -1 +0,0 @@ --{"version":3,"sources":["../src/gas-util.ts"],"sourcesContent":["import {\n query,\n handleFetch,\n gweiDecToWEIBN,\n weiHexToGweiDec,\n} from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport BN from 'bn.js';\n\nimport type {\n GasFeeEstimates,\n EthGasPriceEstimate,\n EstimatedGasFeeTimeBounds,\n unknownString,\n LegacyGasPriceEstimate,\n} from './GasFeeController';\n\nconst makeClientIdHeader = (clientId: string) => ({ 'X-Client-Id': clientId });\n\n/**\n * Convert a decimal GWEI value to a decimal string rounded to the nearest WEI.\n *\n * @param n - The input GWEI amount, as a decimal string or a number.\n * @returns The decimal string GWEI amount.\n */\nexport function normalizeGWEIDecimalNumbers(n: string | number) {\n const numberAsWEIHex = gweiDecToWEIBN(n).toString(16);\n const numberAsGWEI = weiHexToGweiDec(numberAsWEIHex);\n return numberAsGWEI;\n}\n\n/**\n * Fetch gas estimates from the given URL.\n *\n * @param url - The gas estimate URL.\n * @param infuraAPIKey - The Infura API key used for infura API requests.\n * @param clientId - The client ID used to identify to the API who is asking for estimates.\n * @returns The gas estimates.\n */\nexport async function fetchGasEstimates(\n url: string,\n infuraAPIKey: string,\n clientId?: string,\n): Promise {\n const infuraAuthToken = buildInfuraAuthToken(infuraAPIKey);\n const estimates = await handleFetch(url, {\n headers: getHeaders(infuraAuthToken, clientId),\n });\n return {\n low: {\n ...estimates.low,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.low.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.low.suggestedMaxFeePerGas,\n ),\n },\n medium: {\n ...estimates.medium,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.medium.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.medium.suggestedMaxFeePerGas,\n ),\n },\n high: {\n ...estimates.high,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.high.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.high.suggestedMaxFeePerGas,\n ),\n },\n estimatedBaseFee: normalizeGWEIDecimalNumbers(estimates.estimatedBaseFee),\n historicalBaseFeeRange: estimates.historicalBaseFeeRange,\n baseFeeTrend: estimates.baseFeeTrend,\n latestPriorityFeeRange: estimates.latestPriorityFeeRange,\n historicalPriorityFeeRange: estimates.historicalPriorityFeeRange,\n priorityFeeTrend: estimates.priorityFeeTrend,\n networkCongestion: estimates.networkCongestion,\n };\n}\n\n/**\n * Hit the legacy MetaSwaps gasPrices estimate api and return the low, medium\n * high values from that API.\n *\n * @param url - The URL to fetch gas price estimates from.\n * @param infuraAPIKey - The Infura API key used for infura API requests.\n * @param clientId - The client ID used to identify to the API who is asking for estimates.\n * @returns The gas price estimates.\n */\nexport async function fetchLegacyGasPriceEstimates(\n url: string,\n infuraAPIKey: string,\n clientId?: string,\n): Promise {\n const infuraAuthToken = buildInfuraAuthToken(infuraAPIKey);\n const result = await handleFetch(url, {\n referrer: url,\n referrerPolicy: 'no-referrer-when-downgrade',\n method: 'GET',\n mode: 'cors',\n headers: getHeaders(infuraAuthToken, clientId),\n });\n return {\n low: result.SafeGasPrice,\n medium: result.ProposeGasPrice,\n high: result.FastGasPrice,\n };\n}\n\n/**\n * Get a gas price estimate from the network using the `eth_gasPrice` method.\n *\n * @param ethQuery - The EthQuery instance to call the network with.\n * @returns A gas price estimate.\n */\nexport async function fetchEthGasPriceEstimate(\n ethQuery: EthQuery,\n): Promise {\n const gasPrice = await query(ethQuery, 'gasPrice');\n return {\n gasPrice: weiHexToGweiDec(gasPrice).toString(),\n };\n}\n\n/**\n * Estimate the time it will take for a transaction to be confirmed.\n *\n * @param maxPriorityFeePerGas - The max priority fee per gas.\n * @param maxFeePerGas - The max fee per gas.\n * @param gasFeeEstimates - The gas fee estimates.\n * @returns The estimated lower and upper bounds for when this transaction will be confirmed.\n */\nexport function calculateTimeEstimate(\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n gasFeeEstimates: GasFeeEstimates,\n): EstimatedGasFeeTimeBounds {\n const { low, medium, high, estimatedBaseFee } = gasFeeEstimates;\n\n const maxPriorityFeePerGasInWEI = gweiDecToWEIBN(maxPriorityFeePerGas);\n const maxFeePerGasInWEI = gweiDecToWEIBN(maxFeePerGas);\n const estimatedBaseFeeInWEI = gweiDecToWEIBN(estimatedBaseFee);\n\n const effectiveMaxPriorityFee = BN.min(\n maxPriorityFeePerGasInWEI,\n maxFeePerGasInWEI.sub(estimatedBaseFeeInWEI),\n );\n\n const lowMaxPriorityFeeInWEI = gweiDecToWEIBN(\n low.suggestedMaxPriorityFeePerGas,\n );\n const mediumMaxPriorityFeeInWEI = gweiDecToWEIBN(\n medium.suggestedMaxPriorityFeePerGas,\n );\n const highMaxPriorityFeeInWEI = gweiDecToWEIBN(\n high.suggestedMaxPriorityFeePerGas,\n );\n\n let lowerTimeBound;\n let upperTimeBound;\n\n if (effectiveMaxPriorityFee.lt(lowMaxPriorityFeeInWEI)) {\n lowerTimeBound = null;\n upperTimeBound = 'unknown' as unknownString;\n } else if (\n effectiveMaxPriorityFee.gte(lowMaxPriorityFeeInWEI) &&\n effectiveMaxPriorityFee.lt(mediumMaxPriorityFeeInWEI)\n ) {\n lowerTimeBound = low.minWaitTimeEstimate;\n upperTimeBound = low.maxWaitTimeEstimate;\n } else if (\n effectiveMaxPriorityFee.gte(mediumMaxPriorityFeeInWEI) &&\n effectiveMaxPriorityFee.lt(highMaxPriorityFeeInWEI)\n ) {\n lowerTimeBound = medium.minWaitTimeEstimate;\n upperTimeBound = medium.maxWaitTimeEstimate;\n } else if (effectiveMaxPriorityFee.eq(highMaxPriorityFeeInWEI)) {\n lowerTimeBound = high.minWaitTimeEstimate;\n upperTimeBound = high.maxWaitTimeEstimate;\n } else {\n lowerTimeBound = 0;\n upperTimeBound = high.maxWaitTimeEstimate;\n }\n\n return {\n lowerTimeBound,\n upperTimeBound,\n };\n}\n\n/**\n * Build an infura auth token from the given API key and secret.\n *\n * @param infuraAPIKey - The Infura API key.\n * @returns The base64 encoded auth token.\n */\nfunction buildInfuraAuthToken(infuraAPIKey: string) {\n // We intentionally leave the password empty, as Infura does not require one\n return Buffer.from(`${infuraAPIKey}:`).toString('base64');\n}\n\n/**\n * Get the headers for a request to the gas fee API.\n *\n * @param infuraAuthToken - The Infura auth token to use for the request.\n * @param clientId - The client ID used to identify to the API who is asking for estimates.\n * @returns The headers for the request.\n */\nfunction getHeaders(infuraAuthToken: string, clientId?: string) {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Basic ${infuraAuthToken}`,\n // Only add the clientId header if clientId is a non-empty string\n ...(clientId?.trim() ? makeClientIdHeader(clientId) : {}),\n };\n}\n"],"mappings":"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","names":[]} -\ No newline at end of file -diff --git a/dist/chunk-Q2YPK5SL.js b/dist/chunk-Q2YPK5SL.js -deleted file mode 100644 -index 154d3eafb98cdb84cda1a2ea60f6be8a75f4e7a6..0000000000000000000000000000000000000000 ---- a/dist/chunk-Q2YPK5SL.js -+++ /dev/null -@@ -1,165 +0,0 @@ --"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }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/gas-util.ts -- -- -- -- -- --var _controllerutils = require('@metamask/controller-utils'); --var _bnjs = require('bn.js'); var _bnjs2 = _interopRequireDefault(_bnjs); --var makeClientIdHeader = (clientId) => ({ "X-Client-Id": clientId }); --function normalizeGWEIDecimalNumbers(n) { -- const numberAsWEIHex = _controllerutils.gweiDecToWEIBN.call(void 0, n).toString(16); -- const numberAsGWEI = _controllerutils.weiHexToGweiDec.call(void 0, numberAsWEIHex); -- return numberAsGWEI; --} --async function fetchGasEstimates(url, infuraAPIKey, clientId) { -- const infuraAuthToken = buildInfuraAuthToken(infuraAPIKey); -- const estimates = await _controllerutils.handleFetch.call(void 0, url, { -- headers: getHeaders(infuraAuthToken, clientId) -- }); -- return { -- low: { -- ...estimates.low, -- suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.low.suggestedMaxPriorityFeePerGas -- ), -- suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.low.suggestedMaxFeePerGas -- ) -- }, -- medium: { -- ...estimates.medium, -- suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.medium.suggestedMaxPriorityFeePerGas -- ), -- suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.medium.suggestedMaxFeePerGas -- ) -- }, -- high: { -- ...estimates.high, -- suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.high.suggestedMaxPriorityFeePerGas -- ), -- suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -- estimates.high.suggestedMaxFeePerGas -- ) -- }, -- estimatedBaseFee: normalizeGWEIDecimalNumbers(estimates.estimatedBaseFee), -- historicalBaseFeeRange: estimates.historicalBaseFeeRange, -- baseFeeTrend: estimates.baseFeeTrend, -- latestPriorityFeeRange: estimates.latestPriorityFeeRange, -- historicalPriorityFeeRange: estimates.historicalPriorityFeeRange, -- priorityFeeTrend: estimates.priorityFeeTrend, -- networkCongestion: estimates.networkCongestion -- }; --} --async function fetchLegacyGasPriceEstimates(url, infuraAPIKey, clientId) { -- const infuraAuthToken = buildInfuraAuthToken(infuraAPIKey); -- const result = await _controllerutils.handleFetch.call(void 0, url, { -- referrer: url, -- referrerPolicy: "no-referrer-when-downgrade", -- method: "GET", -- mode: "cors", -- headers: getHeaders(infuraAuthToken, clientId) -- }); -- return { -- low: result.SafeGasPrice, -- medium: result.ProposeGasPrice, -- high: result.FastGasPrice -- }; --} --async function fetchEthGasPriceEstimate(ethQuery) { -- const gasPrice = await _controllerutils.query.call(void 0, ethQuery, "gasPrice"); -- return { -- gasPrice: _controllerutils.weiHexToGweiDec.call(void 0, gasPrice).toString() -- }; --} --function calculateTimeEstimate(maxPriorityFeePerGas, maxFeePerGas, gasFeeEstimates) { -- const { low, medium, high, estimatedBaseFee } = gasFeeEstimates; -- const maxPriorityFeePerGasInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, maxPriorityFeePerGas); -- const maxFeePerGasInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, maxFeePerGas); -- const estimatedBaseFeeInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, estimatedBaseFee); -- const effectiveMaxPriorityFee = _bnjs2.default.min( -- maxPriorityFeePerGasInWEI, -- maxFeePerGasInWEI.sub(estimatedBaseFeeInWEI) -- ); -- const lowMaxPriorityFeeInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, -- low.suggestedMaxPriorityFeePerGas -- ); -- const mediumMaxPriorityFeeInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, -- medium.suggestedMaxPriorityFeePerGas -- ); -- const highMaxPriorityFeeInWEI = _controllerutils.gweiDecToWEIBN.call(void 0, -- high.suggestedMaxPriorityFeePerGas -- ); -- let lowerTimeBound; -- let upperTimeBound; -- if (effectiveMaxPriorityFee.lt(lowMaxPriorityFeeInWEI)) { -- lowerTimeBound = null; -- upperTimeBound = "unknown"; -- } else if (effectiveMaxPriorityFee.gte(lowMaxPriorityFeeInWEI) && effectiveMaxPriorityFee.lt(mediumMaxPriorityFeeInWEI)) { -- lowerTimeBound = low.minWaitTimeEstimate; -- upperTimeBound = low.maxWaitTimeEstimate; -- } else if (effectiveMaxPriorityFee.gte(mediumMaxPriorityFeeInWEI) && effectiveMaxPriorityFee.lt(highMaxPriorityFeeInWEI)) { -- lowerTimeBound = medium.minWaitTimeEstimate; -- upperTimeBound = medium.maxWaitTimeEstimate; -- } else if (effectiveMaxPriorityFee.eq(highMaxPriorityFeeInWEI)) { -- lowerTimeBound = high.minWaitTimeEstimate; -- upperTimeBound = high.maxWaitTimeEstimate; -- } else { -- lowerTimeBound = 0; -- upperTimeBound = high.maxWaitTimeEstimate; -- } -- return { -- lowerTimeBound, -- upperTimeBound -- }; --} --function buildInfuraAuthToken(infuraAPIKey) { -- return Buffer.from(`${infuraAPIKey}:`).toString("base64"); --} --function getHeaders(infuraAuthToken, clientId) { -- return { -- "Content-Type": "application/json", -- Authorization: `Basic ${infuraAuthToken}`, -- // Only add the clientId header if clientId is a non-empty string -- ...clientId?.trim() ? makeClientIdHeader(clientId) : {} -- }; --} -- -- -- -- -- -- -- -- -- -- -- --exports.__privateGet = __privateGet; exports.__privateAdd = __privateAdd; exports.__privateSet = __privateSet; exports.__privateMethod = __privateMethod; exports.normalizeGWEIDecimalNumbers = normalizeGWEIDecimalNumbers; exports.fetchGasEstimates = fetchGasEstimates; exports.fetchLegacyGasPriceEstimates = fetchLegacyGasPriceEstimates; exports.fetchEthGasPriceEstimate = fetchEthGasPriceEstimate; exports.calculateTimeEstimate = calculateTimeEstimate; --//# sourceMappingURL=chunk-Q2YPK5SL.js.map -\ No newline at end of file -diff --git a/dist/chunk-Q2YPK5SL.js.map b/dist/chunk-Q2YPK5SL.js.map -deleted file mode 100644 -index dc1a17f2cd5fdd749b46fbb546375ea3a1925081..0000000000000000000000000000000000000000 ---- a/dist/chunk-Q2YPK5SL.js.map -+++ /dev/null -@@ -1 +0,0 @@ --{"version":3,"sources":["../src/gas-util.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n query,\n handleFetch,\n gweiDecToWEIBN,\n weiHexToGweiDec,\n} from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport BN from 'bn.js';\n\nimport type {\n GasFeeEstimates,\n EthGasPriceEstimate,\n EstimatedGasFeeTimeBounds,\n unknownString,\n LegacyGasPriceEstimate,\n} from './GasFeeController';\n\nconst makeClientIdHeader = (clientId: string) => ({ 'X-Client-Id': clientId });\n\n/**\n * Convert a decimal GWEI value to a decimal string rounded to the nearest WEI.\n *\n * @param n - The input GWEI amount, as a decimal string or a number.\n * @returns The decimal string GWEI amount.\n */\nexport function normalizeGWEIDecimalNumbers(n: string | number) {\n const numberAsWEIHex = gweiDecToWEIBN(n).toString(16);\n const numberAsGWEI = weiHexToGweiDec(numberAsWEIHex);\n return numberAsGWEI;\n}\n\n/**\n * Fetch gas estimates from the given URL.\n *\n * @param url - The gas estimate URL.\n * @param infuraAPIKey - The Infura API key used for infura API requests.\n * @param clientId - The client ID used to identify to the API who is asking for estimates.\n * @returns The gas estimates.\n */\nexport async function fetchGasEstimates(\n url: string,\n infuraAPIKey: string,\n clientId?: string,\n): Promise {\n const infuraAuthToken = buildInfuraAuthToken(infuraAPIKey);\n const estimates = await handleFetch(url, {\n headers: getHeaders(infuraAuthToken, clientId),\n });\n return {\n low: {\n ...estimates.low,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.low.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.low.suggestedMaxFeePerGas,\n ),\n },\n medium: {\n ...estimates.medium,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.medium.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.medium.suggestedMaxFeePerGas,\n ),\n },\n high: {\n ...estimates.high,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.high.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.high.suggestedMaxFeePerGas,\n ),\n },\n estimatedBaseFee: normalizeGWEIDecimalNumbers(estimates.estimatedBaseFee),\n historicalBaseFeeRange: estimates.historicalBaseFeeRange,\n baseFeeTrend: estimates.baseFeeTrend,\n latestPriorityFeeRange: estimates.latestPriorityFeeRange,\n historicalPriorityFeeRange: estimates.historicalPriorityFeeRange,\n priorityFeeTrend: estimates.priorityFeeTrend,\n networkCongestion: estimates.networkCongestion,\n };\n}\n\n/**\n * Hit the legacy MetaSwaps gasPrices estimate api and return the low, medium\n * high values from that API.\n *\n * @param url - The URL to fetch gas price estimates from.\n * @param infuraAPIKey - The Infura API key used for infura API requests.\n * @param clientId - The client ID used to identify to the API who is asking for estimates.\n * @returns The gas price estimates.\n */\nexport async function fetchLegacyGasPriceEstimates(\n url: string,\n infuraAPIKey: string,\n clientId?: string,\n): Promise {\n const infuraAuthToken = buildInfuraAuthToken(infuraAPIKey);\n const result = await handleFetch(url, {\n referrer: url,\n referrerPolicy: 'no-referrer-when-downgrade',\n method: 'GET',\n mode: 'cors',\n headers: getHeaders(infuraAuthToken, clientId),\n });\n return {\n low: result.SafeGasPrice,\n medium: result.ProposeGasPrice,\n high: result.FastGasPrice,\n };\n}\n\n/**\n * Get a gas price estimate from the network using the `eth_gasPrice` method.\n *\n * @param ethQuery - The EthQuery instance to call the network with.\n * @returns A gas price estimate.\n */\nexport async function fetchEthGasPriceEstimate(\n ethQuery: EthQuery,\n): Promise {\n const gasPrice = await query(ethQuery, 'gasPrice');\n return {\n gasPrice: weiHexToGweiDec(gasPrice).toString(),\n };\n}\n\n/**\n * Estimate the time it will take for a transaction to be confirmed.\n *\n * @param maxPriorityFeePerGas - The max priority fee per gas.\n * @param maxFeePerGas - The max fee per gas.\n * @param gasFeeEstimates - The gas fee estimates.\n * @returns The estimated lower and upper bounds for when this transaction will be confirmed.\n */\nexport function calculateTimeEstimate(\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n gasFeeEstimates: GasFeeEstimates,\n): EstimatedGasFeeTimeBounds {\n const { low, medium, high, estimatedBaseFee } = gasFeeEstimates;\n\n const maxPriorityFeePerGasInWEI = gweiDecToWEIBN(maxPriorityFeePerGas);\n const maxFeePerGasInWEI = gweiDecToWEIBN(maxFeePerGas);\n const estimatedBaseFeeInWEI = gweiDecToWEIBN(estimatedBaseFee);\n\n const effectiveMaxPriorityFee = BN.min(\n maxPriorityFeePerGasInWEI,\n maxFeePerGasInWEI.sub(estimatedBaseFeeInWEI),\n );\n\n const lowMaxPriorityFeeInWEI = gweiDecToWEIBN(\n low.suggestedMaxPriorityFeePerGas,\n );\n const mediumMaxPriorityFeeInWEI = gweiDecToWEIBN(\n medium.suggestedMaxPriorityFeePerGas,\n );\n const highMaxPriorityFeeInWEI = gweiDecToWEIBN(\n high.suggestedMaxPriorityFeePerGas,\n );\n\n let lowerTimeBound;\n let upperTimeBound;\n\n if (effectiveMaxPriorityFee.lt(lowMaxPriorityFeeInWEI)) {\n lowerTimeBound = null;\n upperTimeBound = 'unknown' as unknownString;\n } else if (\n effectiveMaxPriorityFee.gte(lowMaxPriorityFeeInWEI) &&\n effectiveMaxPriorityFee.lt(mediumMaxPriorityFeeInWEI)\n ) {\n lowerTimeBound = low.minWaitTimeEstimate;\n upperTimeBound = low.maxWaitTimeEstimate;\n } else if (\n effectiveMaxPriorityFee.gte(mediumMaxPriorityFeeInWEI) &&\n effectiveMaxPriorityFee.lt(highMaxPriorityFeeInWEI)\n ) {\n lowerTimeBound = medium.minWaitTimeEstimate;\n upperTimeBound = medium.maxWaitTimeEstimate;\n } else if (effectiveMaxPriorityFee.eq(highMaxPriorityFeeInWEI)) {\n lowerTimeBound = high.minWaitTimeEstimate;\n upperTimeBound = high.maxWaitTimeEstimate;\n } else {\n lowerTimeBound = 0;\n upperTimeBound = high.maxWaitTimeEstimate;\n }\n\n return {\n lowerTimeBound,\n upperTimeBound,\n };\n}\n\n/**\n * Build an infura auth token from the given API key and secret.\n *\n * @param infuraAPIKey - The Infura API key.\n * @returns The base64 encoded auth token.\n */\nfunction buildInfuraAuthToken(infuraAPIKey: string) {\n // We intentionally leave the password empty, as Infura does not require one\n return Buffer.from(`${infuraAPIKey}:`).toString('base64');\n}\n\n/**\n * Get the headers for a request to the gas fee API.\n *\n * @param infuraAuthToken - The Infura auth token to use for the request.\n * @param clientId - The client ID used to identify to the API who is asking for estimates.\n * @returns The headers for the request.\n */\nfunction getHeaders(infuraAuthToken: string, clientId?: string) {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Basic ${infuraAuthToken}`,\n // Only add the clientId header if clientId is a non-empty string\n ...(clientId?.trim() ? makeClientIdHeader(clientId) : {}),\n };\n}\n"]} -\ No newline at end of file -diff --git a/dist/chunk-R3IOI7AK.mjs b/dist/chunk-R3IOI7AK.mjs -new file mode 100644 -index 0000000000000000000000000000000000000000..005f6ca08cfdc55aabcf1849e2a3b651ee7d9303 ---- /dev/null -+++ b/dist/chunk-R3IOI7AK.mjs -@@ -0,0 +1,156 @@ -+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/gas-util.ts -+import { -+ query, -+ handleFetch, -+ gweiDecToWEIBN, -+ weiHexToGweiDec -+} from "@metamask/controller-utils"; -+import BN from "bn.js"; -+var makeClientIdHeader = (clientId) => ({ "X-Client-Id": clientId }); -+function normalizeGWEIDecimalNumbers(n) { -+ const numberAsWEIHex = gweiDecToWEIBN(n).toString(16); -+ const numberAsGWEI = weiHexToGweiDec(numberAsWEIHex); -+ return numberAsGWEI; -+} -+async function fetchGasEstimates(url, clientId) { -+ const estimates = await handleFetch( -+ url, -+ clientId ? { headers: makeClientIdHeader(clientId) } : void 0 -+ ); -+ return { -+ low: { -+ ...estimates.low, -+ suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.low.suggestedMaxPriorityFeePerGas -+ ), -+ suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.low.suggestedMaxFeePerGas -+ ) -+ }, -+ medium: { -+ ...estimates.medium, -+ suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.medium.suggestedMaxPriorityFeePerGas -+ ), -+ suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.medium.suggestedMaxFeePerGas -+ ) -+ }, -+ high: { -+ ...estimates.high, -+ suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.high.suggestedMaxPriorityFeePerGas -+ ), -+ suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers( -+ estimates.high.suggestedMaxFeePerGas -+ ) -+ }, -+ estimatedBaseFee: normalizeGWEIDecimalNumbers(estimates.estimatedBaseFee), -+ historicalBaseFeeRange: estimates.historicalBaseFeeRange, -+ baseFeeTrend: estimates.baseFeeTrend, -+ latestPriorityFeeRange: estimates.latestPriorityFeeRange, -+ historicalPriorityFeeRange: estimates.historicalPriorityFeeRange, -+ priorityFeeTrend: estimates.priorityFeeTrend, -+ networkCongestion: estimates.networkCongestion -+ }; -+} -+async function fetchLegacyGasPriceEstimates(url, clientId) { -+ const result = await handleFetch(url, { -+ referrer: url, -+ referrerPolicy: "no-referrer-when-downgrade", -+ method: "GET", -+ mode: "cors", -+ headers: { -+ "Content-Type": "application/json", -+ ...clientId && makeClientIdHeader(clientId) -+ } -+ }); -+ return { -+ low: result.SafeGasPrice, -+ medium: result.ProposeGasPrice, -+ high: result.FastGasPrice -+ }; -+} -+async function fetchEthGasPriceEstimate(ethQuery) { -+ const gasPrice = await query(ethQuery, "gasPrice"); -+ return { -+ gasPrice: weiHexToGweiDec(gasPrice).toString() -+ }; -+} -+function calculateTimeEstimate(maxPriorityFeePerGas, maxFeePerGas, gasFeeEstimates) { -+ const { low, medium, high, estimatedBaseFee } = gasFeeEstimates; -+ const maxPriorityFeePerGasInWEI = gweiDecToWEIBN(maxPriorityFeePerGas); -+ const maxFeePerGasInWEI = gweiDecToWEIBN(maxFeePerGas); -+ const estimatedBaseFeeInWEI = gweiDecToWEIBN(estimatedBaseFee); -+ const effectiveMaxPriorityFee = BN.min( -+ maxPriorityFeePerGasInWEI, -+ maxFeePerGasInWEI.sub(estimatedBaseFeeInWEI) -+ ); -+ const lowMaxPriorityFeeInWEI = gweiDecToWEIBN( -+ low.suggestedMaxPriorityFeePerGas -+ ); -+ const mediumMaxPriorityFeeInWEI = gweiDecToWEIBN( -+ medium.suggestedMaxPriorityFeePerGas -+ ); -+ const highMaxPriorityFeeInWEI = gweiDecToWEIBN( -+ high.suggestedMaxPriorityFeePerGas -+ ); -+ let lowerTimeBound; -+ let upperTimeBound; -+ if (effectiveMaxPriorityFee.lt(lowMaxPriorityFeeInWEI)) { -+ lowerTimeBound = null; -+ upperTimeBound = "unknown"; -+ } else if (effectiveMaxPriorityFee.gte(lowMaxPriorityFeeInWEI) && effectiveMaxPriorityFee.lt(mediumMaxPriorityFeeInWEI)) { -+ lowerTimeBound = low.minWaitTimeEstimate; -+ upperTimeBound = low.maxWaitTimeEstimate; -+ } else if (effectiveMaxPriorityFee.gte(mediumMaxPriorityFeeInWEI) && effectiveMaxPriorityFee.lt(highMaxPriorityFeeInWEI)) { -+ lowerTimeBound = medium.minWaitTimeEstimate; -+ upperTimeBound = medium.maxWaitTimeEstimate; -+ } else if (effectiveMaxPriorityFee.eq(highMaxPriorityFeeInWEI)) { -+ lowerTimeBound = high.minWaitTimeEstimate; -+ upperTimeBound = high.maxWaitTimeEstimate; -+ } else { -+ lowerTimeBound = 0; -+ upperTimeBound = high.maxWaitTimeEstimate; -+ } -+ return { -+ lowerTimeBound, -+ upperTimeBound -+ }; -+} -+ -+export { -+ __privateGet, -+ __privateAdd, -+ __privateSet, -+ __privateMethod, -+ normalizeGWEIDecimalNumbers, -+ fetchGasEstimates, -+ fetchLegacyGasPriceEstimates, -+ fetchEthGasPriceEstimate, -+ calculateTimeEstimate -+}; -+//# sourceMappingURL=chunk-R3IOI7AK.mjs.map -\ No newline at end of file -diff --git a/dist/chunk-R3IOI7AK.mjs.map b/dist/chunk-R3IOI7AK.mjs.map -new file mode 100644 -index 0000000000000000000000000000000000000000..151a4aa1146e106a273c2e7cbfe8e97c7e6ae6b6 ---- /dev/null -+++ b/dist/chunk-R3IOI7AK.mjs.map -@@ -0,0 +1 @@ -+{"version":3,"sources":["../src/gas-util.ts"],"sourcesContent":["import {\n query,\n handleFetch,\n gweiDecToWEIBN,\n weiHexToGweiDec,\n} from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport BN from 'bn.js';\n\nimport type {\n GasFeeEstimates,\n EthGasPriceEstimate,\n EstimatedGasFeeTimeBounds,\n unknownString,\n LegacyGasPriceEstimate,\n} from './GasFeeController';\n\nconst makeClientIdHeader = (clientId: string) => ({ 'X-Client-Id': clientId });\n\n/**\n * Convert a decimal GWEI value to a decimal string rounded to the nearest WEI.\n *\n * @param n - The input GWEI amount, as a decimal string or a number.\n * @returns The decimal string GWEI amount.\n */\nexport function normalizeGWEIDecimalNumbers(n: string | number) {\n const numberAsWEIHex = gweiDecToWEIBN(n).toString(16);\n const numberAsGWEI = weiHexToGweiDec(numberAsWEIHex);\n return numberAsGWEI;\n}\n\n/**\n * Fetch gas estimates from the given URL.\n *\n * @param url - The gas estimate URL.\n * @param clientId - The client ID used to identify to the API who is asking for estimates.\n * @returns The gas estimates.\n */\nexport async function fetchGasEstimates(\n url: string,\n clientId?: string,\n): Promise {\n const estimates = await handleFetch(\n url,\n clientId ? { headers: makeClientIdHeader(clientId) } : undefined,\n );\n return {\n low: {\n ...estimates.low,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.low.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.low.suggestedMaxFeePerGas,\n ),\n },\n medium: {\n ...estimates.medium,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.medium.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.medium.suggestedMaxFeePerGas,\n ),\n },\n high: {\n ...estimates.high,\n suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.high.suggestedMaxPriorityFeePerGas,\n ),\n suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(\n estimates.high.suggestedMaxFeePerGas,\n ),\n },\n estimatedBaseFee: normalizeGWEIDecimalNumbers(estimates.estimatedBaseFee),\n historicalBaseFeeRange: estimates.historicalBaseFeeRange,\n baseFeeTrend: estimates.baseFeeTrend,\n latestPriorityFeeRange: estimates.latestPriorityFeeRange,\n historicalPriorityFeeRange: estimates.historicalPriorityFeeRange,\n priorityFeeTrend: estimates.priorityFeeTrend,\n networkCongestion: estimates.networkCongestion,\n };\n}\n\n/**\n * Hit the legacy MetaSwaps gasPrices estimate api and return the low, medium\n * high values from that API.\n *\n * @param url - The URL to fetch gas price estimates from.\n * @param clientId - The client ID used to identify to the API who is asking for estimates.\n * @returns The gas price estimates.\n */\nexport async function fetchLegacyGasPriceEstimates(\n url: string,\n clientId?: string,\n): Promise {\n const result = await handleFetch(url, {\n referrer: url,\n referrerPolicy: 'no-referrer-when-downgrade',\n method: 'GET',\n mode: 'cors',\n headers: {\n 'Content-Type': 'application/json',\n ...(clientId && makeClientIdHeader(clientId)),\n },\n });\n return {\n low: result.SafeGasPrice,\n medium: result.ProposeGasPrice,\n high: result.FastGasPrice,\n };\n}\n\n/**\n * Get a gas price estimate from the network using the `eth_gasPrice` method.\n *\n * @param ethQuery - The EthQuery instance to call the network with.\n * @returns A gas price estimate.\n */\nexport async function fetchEthGasPriceEstimate(\n ethQuery: EthQuery,\n): Promise {\n const gasPrice = await query(ethQuery, 'gasPrice');\n return {\n gasPrice: weiHexToGweiDec(gasPrice).toString(),\n };\n}\n\n/**\n * Estimate the time it will take for a transaction to be confirmed.\n *\n * @param maxPriorityFeePerGas - The max priority fee per gas.\n * @param maxFeePerGas - The max fee per gas.\n * @param gasFeeEstimates - The gas fee estimates.\n * @returns The estimated lower and upper bounds for when this transaction will be confirmed.\n */\nexport function calculateTimeEstimate(\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n gasFeeEstimates: GasFeeEstimates,\n): EstimatedGasFeeTimeBounds {\n const { low, medium, high, estimatedBaseFee } = gasFeeEstimates;\n\n const maxPriorityFeePerGasInWEI = gweiDecToWEIBN(maxPriorityFeePerGas);\n const maxFeePerGasInWEI = gweiDecToWEIBN(maxFeePerGas);\n const estimatedBaseFeeInWEI = gweiDecToWEIBN(estimatedBaseFee);\n\n const effectiveMaxPriorityFee = BN.min(\n maxPriorityFeePerGasInWEI,\n maxFeePerGasInWEI.sub(estimatedBaseFeeInWEI),\n );\n\n const lowMaxPriorityFeeInWEI = gweiDecToWEIBN(\n low.suggestedMaxPriorityFeePerGas,\n );\n const mediumMaxPriorityFeeInWEI = gweiDecToWEIBN(\n medium.suggestedMaxPriorityFeePerGas,\n );\n const highMaxPriorityFeeInWEI = gweiDecToWEIBN(\n high.suggestedMaxPriorityFeePerGas,\n );\n\n let lowerTimeBound;\n let upperTimeBound;\n\n if (effectiveMaxPriorityFee.lt(lowMaxPriorityFeeInWEI)) {\n lowerTimeBound = null;\n upperTimeBound = 'unknown' as unknownString;\n } else if (\n effectiveMaxPriorityFee.gte(lowMaxPriorityFeeInWEI) &&\n effectiveMaxPriorityFee.lt(mediumMaxPriorityFeeInWEI)\n ) {\n lowerTimeBound = low.minWaitTimeEstimate;\n upperTimeBound = low.maxWaitTimeEstimate;\n } else if (\n effectiveMaxPriorityFee.gte(mediumMaxPriorityFeeInWEI) &&\n effectiveMaxPriorityFee.lt(highMaxPriorityFeeInWEI)\n ) {\n lowerTimeBound = medium.minWaitTimeEstimate;\n upperTimeBound = medium.maxWaitTimeEstimate;\n } else if (effectiveMaxPriorityFee.eq(highMaxPriorityFeeInWEI)) {\n lowerTimeBound = high.minWaitTimeEstimate;\n upperTimeBound = high.maxWaitTimeEstimate;\n } else {\n lowerTimeBound = 0;\n upperTimeBound = high.maxWaitTimeEstimate;\n }\n\n return {\n lowerTimeBound,\n upperTimeBound,\n };\n}\n"],"mappings":"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","names":[]} -\ No newline at end of file -diff --git a/dist/chunk-X74LQX2Y.js b/dist/chunk-X74LQX2Y.js -new file mode 100644 -index 0000000000000000000000000000000000000000..11d4e4b36c51872b5b529eb4e7d4f7eea91e87c7 ---- /dev/null -+++ b/dist/chunk-X74LQX2Y.js -@@ -0,0 +1,390 @@ -+"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -+ -+ -+ -+ -+ -+ -+ -+ -+var _chunk2MFVV2BXjs = require('./chunk-2MFVV2BX.js'); -+ -+// src/GasFeeController.ts -+ -+ -+ -+ -+var _controllerutils = require('@metamask/controller-utils'); -+var _ethquery = require('@metamask/eth-query'); var _ethquery2 = _interopRequireDefault(_ethquery); -+var _pollingcontroller = require('@metamask/polling-controller'); -+var _uuid = require('uuid'); -+var LEGACY_GAS_PRICES_API_URL = `https://api.metaswap.codefi.network/gasPrices`; -+var GAS_ESTIMATE_TYPES = { -+ FEE_MARKET: "fee-market", -+ LEGACY: "legacy", -+ ETH_GASPRICE: "eth_gasPrice", -+ NONE: "none" -+}; -+var metadata = { -+ gasFeeEstimatesByChainId: { -+ persist: true, -+ anonymous: false -+ }, -+ gasFeeEstimates: { persist: true, anonymous: false }, -+ estimatedGasFeeTimeBounds: { persist: true, anonymous: false }, -+ gasEstimateType: { persist: true, anonymous: false }, -+ nonRPCGasFeeApisDisabled: { persist: true, anonymous: false } -+}; -+var name = "GasFeeController"; -+var defaultState = { -+ gasFeeEstimatesByChainId: {}, -+ gasFeeEstimates: {}, -+ estimatedGasFeeTimeBounds: {}, -+ gasEstimateType: GAS_ESTIMATE_TYPES.NONE, -+ nonRPCGasFeeApisDisabled: false -+}; -+var _getProvider, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn; -+var GasFeeController = class extends _pollingcontroller.StaticIntervalPollingController { -+ /** -+ * Creates a GasFeeController instance. -+ * -+ * @param options - The controller options. -+ * @param options.interval - The time in milliseconds to wait between polls. -+ * @param options.messenger - The controller messenger. -+ * @param options.state - The initial state. -+ * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current -+ * network is EIP-1559 compatible. -+ * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the -+ * current network is compatible with the legacy gas price API. -+ * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current -+ * account is EIP-1559 compatible. -+ * @param options.getChainId - Returns the current chain ID. -+ * @param options.getProvider - Returns a network provider for the current network. -+ * @param options.onNetworkDidChange - A function for registering an event handler for the -+ * network state change event. -+ * @param options.legacyAPIEndpoint - The legacy gas price API URL. This option is primarily for -+ * testing purposes. -+ * @param options.EIP1559APIEndpoint - The EIP-1559 gas price API URL. -+ * @param options.clientId - The client ID used to identify to the gas estimation API who is -+ * asking for estimates. -+ */ -+ constructor({ -+ interval = 15e3, -+ messenger, -+ state, -+ getCurrentNetworkEIP1559Compatibility, -+ getCurrentAccountEIP1559Compatibility, -+ getChainId, -+ getCurrentNetworkLegacyGasAPICompatibility, -+ getProvider, -+ onNetworkDidChange, -+ legacyAPIEndpoint = LEGACY_GAS_PRICES_API_URL, -+ EIP1559APIEndpoint, -+ clientId -+ }) { -+ super({ -+ name, -+ metadata, -+ messenger, -+ state: { ...defaultState, ...state } -+ }); -+ _chunk2MFVV2BXjs.__privateAdd.call(void 0, this, _onNetworkControllerDidChange); -+ _chunk2MFVV2BXjs.__privateAdd.call(void 0, this, _getProvider, void 0); -+ this.intervalDelay = interval; -+ this.setIntervalLength(interval); -+ this.pollTokens = /* @__PURE__ */ new Set(); -+ this.getCurrentNetworkEIP1559Compatibility = getCurrentNetworkEIP1559Compatibility; -+ this.getCurrentNetworkLegacyGasAPICompatibility = getCurrentNetworkLegacyGasAPICompatibility; -+ this.getCurrentAccountEIP1559Compatibility = getCurrentAccountEIP1559Compatibility; -+ _chunk2MFVV2BXjs.__privateSet.call(void 0, this, _getProvider, getProvider); -+ this.EIP1559APIEndpoint = EIP1559APIEndpoint; -+ this.legacyAPIEndpoint = legacyAPIEndpoint; -+ this.clientId = clientId; -+ this.ethQuery = new (0, _ethquery2.default)(_chunk2MFVV2BXjs.__privateGet.call(void 0, this, _getProvider).call(this)); -+ if (onNetworkDidChange && getChainId) { -+ this.currentChainId = getChainId(); -+ onNetworkDidChange(async (networkControllerState) => { -+ await _chunk2MFVV2BXjs.__privateMethod.call(void 0, this, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn).call(this, networkControllerState); -+ }); -+ } else { -+ this.currentChainId = this.messagingSystem.call( -+ "NetworkController:getState" -+ ).providerConfig.chainId; -+ this.messagingSystem.subscribe( -+ "NetworkController:networkDidChange", -+ async (networkControllerState) => { -+ await _chunk2MFVV2BXjs.__privateMethod.call(void 0, this, _onNetworkControllerDidChange, onNetworkControllerDidChange_fn).call(this, networkControllerState); -+ } -+ ); -+ } -+ } -+ async resetPolling() { -+ if (this.pollTokens.size !== 0) { -+ const tokens = Array.from(this.pollTokens); -+ this.stopPolling(); -+ await this.getGasFeeEstimatesAndStartPolling(tokens[0]); -+ tokens.slice(1).forEach((token) => { -+ this.pollTokens.add(token); -+ }); -+ } -+ } -+ async fetchGasFeeEstimates(options) { -+ return await this._fetchGasFeeEstimateData(options); -+ } -+ async getGasFeeEstimatesAndStartPolling(pollToken) { -+ const _pollToken = pollToken || _uuid.v1.call(void 0, ); -+ this.pollTokens.add(_pollToken); -+ if (this.pollTokens.size === 1) { -+ await this._fetchGasFeeEstimateData(); -+ this._poll(); -+ } -+ return _pollToken; -+ } -+ /** -+ * Gets and sets gasFeeEstimates in state. -+ * -+ * @param options - The gas fee estimate options. -+ * @param options.shouldUpdateState - Determines whether the state should be updated with the -+ * updated gas estimates. -+ * @returns The gas fee estimates. -+ */ -+ async _fetchGasFeeEstimateData(options = {}) { -+ const { shouldUpdateState = true, networkClientId } = options; -+ let ethQuery, isEIP1559Compatible, isLegacyGasAPICompatible, decimalChainId; -+ if (networkClientId !== void 0) { -+ const networkClient = this.messagingSystem.call( -+ "NetworkController:getNetworkClientById", -+ networkClientId -+ ); -+ isLegacyGasAPICompatible = networkClient.configuration.chainId === "0x38"; -+ decimalChainId = _controllerutils.convertHexToDecimal.call(void 0, networkClient.configuration.chainId); -+ try { -+ const result = await this.messagingSystem.call( -+ "NetworkController:getEIP1559Compatibility", -+ networkClientId -+ ); -+ isEIP1559Compatible = result || false; -+ } catch { -+ isEIP1559Compatible = false; -+ } -+ ethQuery = new (0, _ethquery2.default)(networkClient.provider); -+ } -+ ethQuery ?? (ethQuery = this.ethQuery); -+ isLegacyGasAPICompatible ?? (isLegacyGasAPICompatible = this.getCurrentNetworkLegacyGasAPICompatibility()); -+ decimalChainId ?? (decimalChainId = _controllerutils.convertHexToDecimal.call(void 0, this.currentChainId)); -+ try { -+ isEIP1559Compatible ?? (isEIP1559Compatible = await this.getEIP1559Compatibility()); -+ } catch (e) { -+ console.error(e); -+ isEIP1559Compatible ?? (isEIP1559Compatible = false); -+ } -+ const gasFeeCalculations = await determineGasFeeCalculations({ -+ isEIP1559Compatible, -+ isLegacyGasAPICompatible, -+ fetchGasEstimates: _chunk2MFVV2BXjs.fetchGasEstimates, -+ fetchGasEstimatesUrl: this.EIP1559APIEndpoint.replace( -+ "", -+ `${decimalChainId}` -+ ), -+ fetchLegacyGasPriceEstimates: _chunk2MFVV2BXjs.fetchLegacyGasPriceEstimates, -+ fetchLegacyGasPriceEstimatesUrl: this.legacyAPIEndpoint.replace( -+ "", -+ `${decimalChainId}` -+ ), -+ fetchEthGasPriceEstimate: _chunk2MFVV2BXjs.fetchEthGasPriceEstimate, -+ calculateTimeEstimate: _chunk2MFVV2BXjs.calculateTimeEstimate, -+ clientId: this.clientId, -+ ethQuery, -+ nonRPCGasFeeApisDisabled: this.state.nonRPCGasFeeApisDisabled -+ }); -+ if (shouldUpdateState) { -+ const chainId = _controllerutils.toHex.call(void 0, decimalChainId); -+ this.update((state) => { -+ if (this.currentChainId === chainId) { -+ state.gasFeeEstimates = gasFeeCalculations.gasFeeEstimates; -+ state.estimatedGasFeeTimeBounds = gasFeeCalculations.estimatedGasFeeTimeBounds; -+ state.gasEstimateType = gasFeeCalculations.gasEstimateType; -+ } -+ state.gasFeeEstimatesByChainId ?? (state.gasFeeEstimatesByChainId = {}); -+ state.gasFeeEstimatesByChainId[chainId] = { -+ gasFeeEstimates: gasFeeCalculations.gasFeeEstimates, -+ estimatedGasFeeTimeBounds: gasFeeCalculations.estimatedGasFeeTimeBounds, -+ gasEstimateType: gasFeeCalculations.gasEstimateType -+ }; -+ }); -+ } -+ return gasFeeCalculations; -+ } -+ /** -+ * Remove the poll token, and stop polling if the set of poll tokens is empty. -+ * -+ * @param pollToken - The poll token to disconnect. -+ */ -+ disconnectPoller(pollToken) { -+ this.pollTokens.delete(pollToken); -+ if (this.pollTokens.size === 0) { -+ this.stopPolling(); -+ } -+ } -+ stopPolling() { -+ if (this.intervalId) { -+ clearInterval(this.intervalId); -+ } -+ this.pollTokens.clear(); -+ this.resetState(); -+ } -+ /** -+ * Prepare to discard this controller. -+ * -+ * This stops any active polling. -+ */ -+ destroy() { -+ super.destroy(); -+ this.stopPolling(); -+ } -+ _poll() { -+ if (this.intervalId) { -+ clearInterval(this.intervalId); -+ } -+ this.intervalId = setInterval(async () => { -+ await _controllerutils.safelyExecute.call(void 0, () => this._fetchGasFeeEstimateData()); -+ }, this.intervalDelay); -+ } -+ /** -+ * Fetching token list from the Token Service API. -+ * -+ * @private -+ * @param networkClientId - The ID of the network client triggering the fetch. -+ * @returns A promise that resolves when this operation completes. -+ */ -+ async _executePoll(networkClientId) { -+ await this._fetchGasFeeEstimateData({ networkClientId }); -+ } -+ resetState() { -+ this.update(() => { -+ return defaultState; -+ }); -+ } -+ async getEIP1559Compatibility() { -+ const currentNetworkIsEIP1559Compatible = await this.getCurrentNetworkEIP1559Compatibility(); -+ const currentAccountIsEIP1559Compatible = this.getCurrentAccountEIP1559Compatibility?.() ?? true; -+ return currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible; -+ } -+ getTimeEstimate(maxPriorityFeePerGas, maxFeePerGas) { -+ if (!this.state.gasFeeEstimates || this.state.gasEstimateType !== GAS_ESTIMATE_TYPES.FEE_MARKET) { -+ return {}; -+ } -+ return _chunk2MFVV2BXjs.calculateTimeEstimate.call(void 0, -+ maxPriorityFeePerGas, -+ maxFeePerGas, -+ this.state.gasFeeEstimates -+ ); -+ } -+ enableNonRPCGasFeeApis() { -+ this.update((state) => { -+ state.nonRPCGasFeeApisDisabled = false; -+ }); -+ } -+ disableNonRPCGasFeeApis() { -+ this.update((state) => { -+ state.nonRPCGasFeeApisDisabled = true; -+ }); -+ } -+}; -+_getProvider = new WeakMap(); -+_onNetworkControllerDidChange = new WeakSet(); -+onNetworkControllerDidChange_fn = async function(networkControllerState) { -+ const newChainId = networkControllerState.providerConfig.chainId; -+ if (newChainId !== this.currentChainId) { -+ this.ethQuery = new (0, _ethquery2.default)(_chunk2MFVV2BXjs.__privateGet.call(void 0, this, _getProvider).call(this)); -+ await this.resetPolling(); -+ this.currentChainId = newChainId; -+ } -+}; -+var GasFeeController_default = GasFeeController; -+ -+// src/determineGasFeeCalculations.ts -+async function determineGasFeeCalculations(args) { -+ try { -+ return await getEstimatesUsingFallbacks(args); -+ } catch (error) { -+ if (error instanceof Error) { -+ throw new Error( -+ `Gas fee/price estimation failed. Message: ${error.message}` -+ ); -+ } -+ throw error; -+ } -+} -+async function getEstimatesUsingFallbacks(request) { -+ const { -+ isEIP1559Compatible, -+ isLegacyGasAPICompatible, -+ nonRPCGasFeeApisDisabled -+ } = request; -+ try { -+ if (isEIP1559Compatible && !nonRPCGasFeeApisDisabled) { -+ return await getEstimatesUsingFeeMarketEndpoint(request); -+ } -+ if (isLegacyGasAPICompatible && !nonRPCGasFeeApisDisabled) { -+ return await getEstimatesUsingLegacyEndpoint(request); -+ } -+ throw new Error("Main gas fee/price estimation failed. Use fallback"); -+ } catch { -+ return await getEstimatesUsingProvider(request); -+ } -+} -+async function getEstimatesUsingFeeMarketEndpoint(request) { -+ const { -+ fetchGasEstimates: fetchGasEstimates2, -+ fetchGasEstimatesUrl, -+ clientId, -+ calculateTimeEstimate: calculateTimeEstimate2 -+ } = request; -+ const estimates = await fetchGasEstimates2(fetchGasEstimatesUrl, clientId); -+ const { suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas } = estimates.medium; -+ const estimatedGasFeeTimeBounds = calculateTimeEstimate2( -+ suggestedMaxPriorityFeePerGas, -+ suggestedMaxFeePerGas, -+ estimates -+ ); -+ return { -+ gasFeeEstimates: estimates, -+ estimatedGasFeeTimeBounds, -+ gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET -+ }; -+} -+async function getEstimatesUsingLegacyEndpoint(request) { -+ const { -+ fetchLegacyGasPriceEstimates: fetchLegacyGasPriceEstimates2, -+ fetchLegacyGasPriceEstimatesUrl, -+ clientId -+ } = request; -+ const estimates = await fetchLegacyGasPriceEstimates2( -+ fetchLegacyGasPriceEstimatesUrl, -+ clientId -+ ); -+ return { -+ gasFeeEstimates: estimates, -+ estimatedGasFeeTimeBounds: {}, -+ gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY -+ }; -+} -+async function getEstimatesUsingProvider(request) { -+ const { ethQuery, fetchEthGasPriceEstimate: fetchEthGasPriceEstimate2 } = request; -+ const estimates = await fetchEthGasPriceEstimate2(ethQuery); -+ return { -+ gasFeeEstimates: estimates, -+ estimatedGasFeeTimeBounds: {}, -+ gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE -+ }; -+} -+ -+ -+ -+ -+ -+ -+ -+exports.determineGasFeeCalculations = determineGasFeeCalculations; exports.LEGACY_GAS_PRICES_API_URL = LEGACY_GAS_PRICES_API_URL; exports.GAS_ESTIMATE_TYPES = GAS_ESTIMATE_TYPES; exports.GasFeeController = GasFeeController; exports.GasFeeController_default = GasFeeController_default; -+//# sourceMappingURL=chunk-X74LQX2Y.js.map -\ No newline at end of file -diff --git a/dist/chunk-X74LQX2Y.js.map b/dist/chunk-X74LQX2Y.js.map -new file mode 100644 -index 0000000000000000000000000000000000000000..c330267c1f74907a70d669611fb5d8eff71305aa ---- /dev/null -+++ b/dist/chunk-X74LQX2Y.js.map -@@ -0,0 +1 @@ -+{"version":3,"sources":["../src/GasFeeController.ts","../src/determineGasFeeCalculations.ts"],"names":["fetchGasEstimates","calculateTimeEstimate","fetchLegacyGasPriceEstimates","fetchEthGasPriceEstimate"],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport {\n convertHexToDecimal,\n safelyExecute,\n toHex,\n} from '@metamask/controller-utils';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClientId,\n NetworkControllerGetEIP1559CompatibilityAction,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerNetworkDidChangeEvent,\n NetworkState,\n ProviderProxy,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { Hex } from '@metamask/utils';\nimport { v1 as random } from 'uuid';\n\nimport determineGasFeeCalculations from './determineGasFeeCalculations';\nimport {\n fetchGasEstimates,\n fetchLegacyGasPriceEstimates,\n fetchEthGasPriceEstimate,\n calculateTimeEstimate,\n} from './gas-util';\n\nexport const LEGACY_GAS_PRICES_API_URL = `https://api.metaswap.codefi.network/gasPrices`;\n\nexport type unknownString = 'unknown';\n\n// Fee Market describes the way gas is set after the london hardfork, and was\n// defined by EIP-1559.\nexport type FeeMarketEstimateType = 'fee-market';\n// Legacy describes gasPrice estimates from before london hardfork, when the\n// user is connected to mainnet and are presented with fast/average/slow\n// estimate levels to choose from.\nexport type LegacyEstimateType = 'legacy';\n// EthGasPrice describes a gasPrice estimate received from eth_gasPrice. Post\n// london this value should only be used for legacy type transactions when on\n// networks that support EIP-1559. This type of estimate is the most accurate\n// to display on custom networks that don't support EIP-1559.\nexport type EthGasPriceEstimateType = 'eth_gasPrice';\n// NoEstimate describes the state of the controller before receiving its first\n// estimate.\nexport type NoEstimateType = 'none';\n\n/**\n * Indicates which type of gasEstimate the controller is currently returning.\n * This is useful as a way of asserting that the shape of gasEstimates matches\n * expectations. NONE is a special case indicating that no previous gasEstimate\n * has been fetched.\n */\nexport const GAS_ESTIMATE_TYPES = {\n FEE_MARKET: 'fee-market' as FeeMarketEstimateType,\n LEGACY: 'legacy' as LegacyEstimateType,\n ETH_GASPRICE: 'eth_gasPrice' as EthGasPriceEstimateType,\n NONE: 'none' as NoEstimateType,\n};\n\nexport type GasEstimateType =\n | FeeMarketEstimateType\n | EthGasPriceEstimateType\n | LegacyEstimateType\n | NoEstimateType;\n\nexport type EstimatedGasFeeTimeBounds = {\n lowerTimeBound: number | null;\n upperTimeBound: number | unknownString;\n};\n\n/**\n * @type EthGasPriceEstimate\n *\n * A single gas price estimate for networks and accounts that don't support EIP-1559\n * This estimate comes from eth_gasPrice but is converted to dec gwei to match other\n * return values\n * @property gasPrice - A GWEI dec string\n */\n\nexport type EthGasPriceEstimate = {\n gasPrice: string;\n};\n\n/**\n * @type LegacyGasPriceEstimate\n *\n * A set of gas price estimates for networks and accounts that don't support EIP-1559\n * These estimates include low, medium and high all as strings representing gwei in\n * decimal format.\n * @property high - gasPrice, in decimal gwei string format, suggested for fast inclusion\n * @property medium - gasPrice, in decimal gwei string format, suggested for avg inclusion\n * @property low - gasPrice, in decimal gwei string format, suggested for slow inclusion\n */\nexport type LegacyGasPriceEstimate = {\n high: string;\n medium: string;\n low: string;\n};\n\n/**\n * @type Eip1559GasFee\n *\n * Data necessary to provide an estimate of a gas fee with a specific tip\n * @property minWaitTimeEstimate - The fastest the transaction will take, in milliseconds\n * @property maxWaitTimeEstimate - The slowest the transaction will take, in milliseconds\n * @property suggestedMaxPriorityFeePerGas - A suggested \"tip\", a GWEI hex number\n * @property suggestedMaxFeePerGas - A suggested max fee, the most a user will pay. a GWEI hex number\n */\nexport type Eip1559GasFee = {\n minWaitTimeEstimate: number; // a time duration in milliseconds\n maxWaitTimeEstimate: number; // a time duration in milliseconds\n suggestedMaxPriorityFeePerGas: string; // a GWEI decimal number\n suggestedMaxFeePerGas: string; // a GWEI decimal number\n};\n\n/**\n * @type GasFeeEstimates\n *\n * Data necessary to provide multiple GasFee estimates, and supporting information, to the user\n * @property low - A GasFee for a minimum necessary combination of tip and maxFee\n * @property medium - A GasFee for a recommended combination of tip and maxFee\n * @property high - A GasFee for a high combination of tip and maxFee\n * @property estimatedBaseFee - An estimate of what the base fee will be for the pending/next block. A GWEI dec number\n * @property networkCongestion - A normalized number that can be used to gauge the congestion\n * level of the network, with 0 meaning not congested and 1 meaning extremely congested\n */\nexport type GasFeeEstimates = SourcedGasFeeEstimates | FallbackGasFeeEstimates;\n\ntype SourcedGasFeeEstimates = {\n low: Eip1559GasFee;\n medium: Eip1559GasFee;\n high: Eip1559GasFee;\n estimatedBaseFee: string;\n historicalBaseFeeRange: [string, string];\n baseFeeTrend: 'up' | 'down' | 'level';\n latestPriorityFeeRange: [string, string];\n historicalPriorityFeeRange: [string, string];\n priorityFeeTrend: 'up' | 'down' | 'level';\n networkCongestion: number;\n};\n\ntype FallbackGasFeeEstimates = {\n low: Eip1559GasFee;\n medium: Eip1559GasFee;\n high: Eip1559GasFee;\n estimatedBaseFee: string;\n historicalBaseFeeRange: null;\n baseFeeTrend: null;\n latestPriorityFeeRange: null;\n historicalPriorityFeeRange: null;\n priorityFeeTrend: null;\n networkCongestion: null;\n};\n\nconst metadata = {\n gasFeeEstimatesByChainId: {\n persist: true,\n anonymous: false,\n },\n gasFeeEstimates: { persist: true, anonymous: false },\n estimatedGasFeeTimeBounds: { persist: true, anonymous: false },\n gasEstimateType: { persist: true, anonymous: false },\n nonRPCGasFeeApisDisabled: { persist: true, anonymous: false },\n};\n\nexport type GasFeeStateEthGasPrice = {\n gasFeeEstimates: EthGasPriceEstimate;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: EthGasPriceEstimateType;\n};\n\nexport type GasFeeStateFeeMarket = {\n gasFeeEstimates: GasFeeEstimates;\n estimatedGasFeeTimeBounds: EstimatedGasFeeTimeBounds | Record;\n gasEstimateType: FeeMarketEstimateType;\n};\n\nexport type GasFeeStateLegacy = {\n gasFeeEstimates: LegacyGasPriceEstimate;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: LegacyEstimateType;\n};\n\nexport type GasFeeStateNoEstimates = {\n gasFeeEstimates: Record;\n estimatedGasFeeTimeBounds: Record;\n gasEstimateType: NoEstimateType;\n};\n\nexport type FetchGasFeeEstimateOptions = {\n shouldUpdateState?: boolean;\n networkClientId?: NetworkClientId;\n};\n\n/**\n * @type GasFeeState\n *\n * Gas Fee controller state\n * @property gasFeeEstimates - Gas fee estimate data based on new EIP-1559 properties\n * @property estimatedGasFeeTimeBounds - Estimates representing the minimum and maximum\n */\nexport type SingleChainGasFeeState =\n | GasFeeStateEthGasPrice\n | GasFeeStateFeeMarket\n | GasFeeStateLegacy\n | GasFeeStateNoEstimates;\n\nexport type GasFeeEstimatesByChainId = {\n gasFeeEstimatesByChainId?: Record;\n};\n\nexport type GasFeeState = GasFeeEstimatesByChainId &\n SingleChainGasFeeState & {\n nonRPCGasFeeApisDisabled?: boolean;\n };\n\nconst name = 'GasFeeController';\n\nexport type GasFeeStateChange = ControllerStateChangeEvent<\n typeof name,\n GasFeeState\n>;\n\nexport type GetGasFeeState = ControllerGetStateAction;\n\nexport type GasFeeControllerActions = GetGasFeeState;\n\nexport type GasFeeControllerEvents = GasFeeStateChange;\n\ntype AllowedActions =\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetEIP1559CompatibilityAction;\n\ntype GasFeeMessenger = RestrictedControllerMessenger<\n typeof name,\n GasFeeControllerActions | AllowedActions,\n GasFeeControllerEvents | NetworkControllerNetworkDidChangeEvent,\n AllowedActions['type'],\n NetworkControllerNetworkDidChangeEvent['type']\n>;\n\nconst defaultState: GasFeeState = {\n gasFeeEstimatesByChainId: {},\n gasFeeEstimates: {},\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.NONE,\n nonRPCGasFeeApisDisabled: false,\n};\n\n/**\n * Controller that retrieves gas fee estimate data and polls for updated data on a set interval\n */\nexport class GasFeeController extends StaticIntervalPollingController<\n typeof name,\n GasFeeState,\n GasFeeMessenger\n> {\n private intervalId?: ReturnType;\n\n private readonly intervalDelay;\n\n private readonly pollTokens: Set;\n\n private readonly legacyAPIEndpoint: string;\n\n private readonly EIP1559APIEndpoint: string;\n\n private readonly getCurrentNetworkEIP1559Compatibility;\n\n private readonly getCurrentNetworkLegacyGasAPICompatibility;\n\n private readonly getCurrentAccountEIP1559Compatibility;\n\n private currentChainId;\n\n private ethQuery?: EthQuery;\n\n private readonly clientId?: string;\n\n #getProvider: () => ProviderProxy;\n\n /**\n * Creates a GasFeeController instance.\n *\n * @param options - The controller options.\n * @param options.interval - The time in milliseconds to wait between polls.\n * @param options.messenger - The controller messenger.\n * @param options.state - The initial state.\n * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current\n * network is EIP-1559 compatible.\n * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the\n * current network is compatible with the legacy gas price API.\n * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current\n * account is EIP-1559 compatible.\n * @param options.getChainId - Returns the current chain ID.\n * @param options.getProvider - Returns a network provider for the current network.\n * @param options.onNetworkDidChange - A function for registering an event handler for the\n * network state change event.\n * @param options.legacyAPIEndpoint - The legacy gas price API URL. This option is primarily for\n * testing purposes.\n * @param options.EIP1559APIEndpoint - The EIP-1559 gas price API URL.\n * @param options.clientId - The client ID used to identify to the gas estimation API who is\n * asking for estimates.\n */\n constructor({\n interval = 15000,\n messenger,\n state,\n getCurrentNetworkEIP1559Compatibility,\n getCurrentAccountEIP1559Compatibility,\n getChainId,\n getCurrentNetworkLegacyGasAPICompatibility,\n getProvider,\n onNetworkDidChange,\n legacyAPIEndpoint = LEGACY_GAS_PRICES_API_URL,\n EIP1559APIEndpoint,\n clientId,\n }: {\n interval?: number;\n messenger: GasFeeMessenger;\n state?: GasFeeState;\n getCurrentNetworkEIP1559Compatibility: () => Promise;\n getCurrentNetworkLegacyGasAPICompatibility: () => boolean;\n getCurrentAccountEIP1559Compatibility?: () => boolean;\n getChainId?: () => Hex;\n getProvider: () => ProviderProxy;\n onNetworkDidChange?: (listener: (state: NetworkState) => void) => void;\n legacyAPIEndpoint?: string;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n EIP1559APIEndpoint: string;\n clientId?: string;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.intervalDelay = interval;\n this.setIntervalLength(interval);\n this.pollTokens = new Set();\n this.getCurrentNetworkEIP1559Compatibility =\n getCurrentNetworkEIP1559Compatibility;\n this.getCurrentNetworkLegacyGasAPICompatibility =\n getCurrentNetworkLegacyGasAPICompatibility;\n this.getCurrentAccountEIP1559Compatibility =\n getCurrentAccountEIP1559Compatibility;\n this.#getProvider = getProvider;\n this.EIP1559APIEndpoint = EIP1559APIEndpoint;\n this.legacyAPIEndpoint = legacyAPIEndpoint;\n this.clientId = clientId;\n\n this.ethQuery = new EthQuery(this.#getProvider());\n\n if (onNetworkDidChange && getChainId) {\n this.currentChainId = getChainId();\n onNetworkDidChange(async (networkControllerState) => {\n await this.#onNetworkControllerDidChange(networkControllerState);\n });\n } else {\n this.currentChainId = this.messagingSystem.call(\n 'NetworkController:getState',\n ).providerConfig.chainId;\n this.messagingSystem.subscribe(\n 'NetworkController:networkDidChange',\n async (networkControllerState) => {\n await this.#onNetworkControllerDidChange(networkControllerState);\n },\n );\n }\n }\n\n async resetPolling() {\n if (this.pollTokens.size !== 0) {\n const tokens = Array.from(this.pollTokens);\n this.stopPolling();\n await this.getGasFeeEstimatesAndStartPolling(tokens[0]);\n tokens.slice(1).forEach((token) => {\n this.pollTokens.add(token);\n });\n }\n }\n\n async fetchGasFeeEstimates(options?: FetchGasFeeEstimateOptions) {\n return await this._fetchGasFeeEstimateData(options);\n }\n\n async getGasFeeEstimatesAndStartPolling(\n pollToken: string | undefined,\n ): Promise {\n const _pollToken = pollToken || random();\n\n this.pollTokens.add(_pollToken);\n\n if (this.pollTokens.size === 1) {\n await this._fetchGasFeeEstimateData();\n this._poll();\n }\n\n return _pollToken;\n }\n\n /**\n * Gets and sets gasFeeEstimates in state.\n *\n * @param options - The gas fee estimate options.\n * @param options.shouldUpdateState - Determines whether the state should be updated with the\n * updated gas estimates.\n * @returns The gas fee estimates.\n */\n async _fetchGasFeeEstimateData(\n options: FetchGasFeeEstimateOptions = {},\n ): Promise {\n const { shouldUpdateState = true, networkClientId } = options;\n\n let ethQuery,\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n decimalChainId: number;\n\n if (networkClientId !== undefined) {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n isLegacyGasAPICompatible = networkClient.configuration.chainId === '0x38';\n\n decimalChainId = convertHexToDecimal(networkClient.configuration.chainId);\n\n try {\n const result = await this.messagingSystem.call(\n 'NetworkController:getEIP1559Compatibility',\n networkClientId,\n );\n isEIP1559Compatible = result || false;\n } catch {\n isEIP1559Compatible = false;\n }\n ethQuery = new EthQuery(networkClient.provider);\n }\n\n ethQuery ??= this.ethQuery;\n\n isLegacyGasAPICompatible ??=\n this.getCurrentNetworkLegacyGasAPICompatibility();\n\n decimalChainId ??= convertHexToDecimal(this.currentChainId);\n\n try {\n isEIP1559Compatible ??= await this.getEIP1559Compatibility();\n } catch (e) {\n console.error(e);\n isEIP1559Compatible ??= false;\n }\n\n const gasFeeCalculations = await determineGasFeeCalculations({\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n fetchGasEstimates,\n fetchGasEstimatesUrl: this.EIP1559APIEndpoint.replace(\n '',\n `${decimalChainId}`,\n ),\n fetchLegacyGasPriceEstimates,\n fetchLegacyGasPriceEstimatesUrl: this.legacyAPIEndpoint.replace(\n '',\n `${decimalChainId}`,\n ),\n fetchEthGasPriceEstimate,\n calculateTimeEstimate,\n clientId: this.clientId,\n ethQuery,\n nonRPCGasFeeApisDisabled: this.state.nonRPCGasFeeApisDisabled,\n });\n\n if (shouldUpdateState) {\n const chainId = toHex(decimalChainId);\n this.update((state) => {\n if (this.currentChainId === chainId) {\n state.gasFeeEstimates = gasFeeCalculations.gasFeeEstimates;\n state.estimatedGasFeeTimeBounds =\n gasFeeCalculations.estimatedGasFeeTimeBounds;\n state.gasEstimateType = gasFeeCalculations.gasEstimateType;\n }\n state.gasFeeEstimatesByChainId ??= {};\n state.gasFeeEstimatesByChainId[chainId] = {\n gasFeeEstimates: gasFeeCalculations.gasFeeEstimates,\n estimatedGasFeeTimeBounds:\n gasFeeCalculations.estimatedGasFeeTimeBounds,\n gasEstimateType: gasFeeCalculations.gasEstimateType,\n } as SingleChainGasFeeState;\n });\n }\n\n return gasFeeCalculations;\n }\n\n /**\n * Remove the poll token, and stop polling if the set of poll tokens is empty.\n *\n * @param pollToken - The poll token to disconnect.\n */\n disconnectPoller(pollToken: string) {\n this.pollTokens.delete(pollToken);\n if (this.pollTokens.size === 0) {\n this.stopPolling();\n }\n }\n\n stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n this.pollTokens.clear();\n this.resetState();\n }\n\n /**\n * Prepare to discard this controller.\n *\n * This stops any active polling.\n */\n override destroy() {\n super.destroy();\n this.stopPolling();\n }\n\n private _poll() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n\n this.intervalId = setInterval(async () => {\n await safelyExecute(() => this._fetchGasFeeEstimateData());\n }, this.intervalDelay);\n }\n\n /**\n * Fetching token list from the Token Service API.\n *\n * @private\n * @param networkClientId - The ID of the network client triggering the fetch.\n * @returns A promise that resolves when this operation completes.\n */\n async _executePoll(networkClientId: string): Promise {\n await this._fetchGasFeeEstimateData({ networkClientId });\n }\n\n private resetState() {\n this.update(() => {\n return defaultState;\n });\n }\n\n private async getEIP1559Compatibility() {\n const currentNetworkIsEIP1559Compatible =\n await this.getCurrentNetworkEIP1559Compatibility();\n const currentAccountIsEIP1559Compatible =\n this.getCurrentAccountEIP1559Compatibility?.() ?? true;\n\n return (\n currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible\n );\n }\n\n getTimeEstimate(\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n ): EstimatedGasFeeTimeBounds | Record {\n if (\n !this.state.gasFeeEstimates ||\n this.state.gasEstimateType !== GAS_ESTIMATE_TYPES.FEE_MARKET\n ) {\n return {};\n }\n return calculateTimeEstimate(\n maxPriorityFeePerGas,\n maxFeePerGas,\n this.state.gasFeeEstimates,\n );\n }\n\n async #onNetworkControllerDidChange(networkControllerState: NetworkState) {\n const newChainId = networkControllerState.providerConfig.chainId;\n\n if (newChainId !== this.currentChainId) {\n this.ethQuery = new EthQuery(this.#getProvider());\n await this.resetPolling();\n\n this.currentChainId = newChainId;\n }\n }\n\n enableNonRPCGasFeeApis() {\n this.update((state) => {\n state.nonRPCGasFeeApisDisabled = false;\n });\n }\n\n disableNonRPCGasFeeApis() {\n this.update((state) => {\n state.nonRPCGasFeeApisDisabled = true;\n });\n }\n}\n\nexport default GasFeeController;\n","import type {\n EstimatedGasFeeTimeBounds,\n EthGasPriceEstimate,\n GasFeeEstimates,\n GasFeeState as GasFeeCalculations,\n LegacyGasPriceEstimate,\n} from './GasFeeController';\nimport { GAS_ESTIMATE_TYPES } from './GasFeeController';\n\ntype DetermineGasFeeCalculationsRequest = {\n isEIP1559Compatible: boolean;\n isLegacyGasAPICompatible: boolean;\n fetchGasEstimates: (\n url: string,\n clientId?: string,\n ) => Promise;\n fetchGasEstimatesUrl: string;\n fetchLegacyGasPriceEstimates: (\n url: string,\n clientId?: string,\n ) => Promise;\n fetchLegacyGasPriceEstimatesUrl: string;\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n fetchEthGasPriceEstimate: (ethQuery: any) => Promise;\n calculateTimeEstimate: (\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n gasFeeEstimates: GasFeeEstimates,\n ) => EstimatedGasFeeTimeBounds;\n clientId: string | undefined;\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ethQuery: any;\n nonRPCGasFeeApisDisabled?: boolean;\n};\n\n/**\n * Obtains a set of max base and priority fee estimates along with time estimates so that we\n * can present them to users when they are sending transactions or making swaps.\n *\n * @param args - The arguments.\n * @param args.isEIP1559Compatible - Governs whether or not we can use an EIP-1559-only method to\n * produce estimates.\n * @param args.isLegacyGasAPICompatible - Governs whether or not we can use a non-EIP-1559 method to\n * produce estimates (for instance, testnets do not support estimates altogether).\n * @param args.fetchGasEstimates - A function that fetches gas estimates using an EIP-1559-specific\n * API.\n * @param args.fetchGasEstimatesUrl - The URL for the API we can use to obtain EIP-1559-specific\n * estimates.\n * @param args.fetchLegacyGasPriceEstimates - A function that fetches gas estimates using an\n * non-EIP-1559-specific API.\n * @param args.fetchLegacyGasPriceEstimatesUrl - The URL for the API we can use to obtain\n * non-EIP-1559-specific estimates.\n * @param args.fetchEthGasPriceEstimate - A function that fetches gas estimates using\n * `eth_gasPrice`.\n * @param args.calculateTimeEstimate - A function that determine time estimate bounds.\n * @param args.clientId - An identifier that an API can use to know who is asking for estimates.\n * @param args.ethQuery - An EthQuery instance we can use to talk to Ethereum directly.\n * @param args.nonRPCGasFeeApisDisabled - Whether to disable requests to the legacyAPIEndpoint and the EIP1559APIEndpoint\n * @returns The gas fee calculations.\n */\nexport default async function determineGasFeeCalculations(\n args: DetermineGasFeeCalculationsRequest,\n): Promise {\n try {\n return await getEstimatesUsingFallbacks(args);\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(\n `Gas fee/price estimation failed. Message: ${error.message}`,\n );\n }\n\n throw error;\n }\n}\n\n/**\n * Retrieve the gas fee estimates using a series of fallback mechanisms.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingFallbacks(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n nonRPCGasFeeApisDisabled,\n } = request;\n\n try {\n if (isEIP1559Compatible && !nonRPCGasFeeApisDisabled) {\n return await getEstimatesUsingFeeMarketEndpoint(request);\n }\n\n if (isLegacyGasAPICompatible && !nonRPCGasFeeApisDisabled) {\n return await getEstimatesUsingLegacyEndpoint(request);\n }\n\n throw new Error('Main gas fee/price estimation failed. Use fallback');\n } catch {\n return await getEstimatesUsingProvider(request);\n }\n}\n\n/**\n * Retrieve gas fee estimates using the EIP-1559 endpoint of the gas API.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingFeeMarketEndpoint(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n fetchGasEstimates,\n fetchGasEstimatesUrl,\n clientId,\n calculateTimeEstimate,\n } = request;\n\n const estimates = await fetchGasEstimates(fetchGasEstimatesUrl, clientId);\n\n const { suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas } =\n estimates.medium;\n\n const estimatedGasFeeTimeBounds = calculateTimeEstimate(\n suggestedMaxPriorityFeePerGas,\n suggestedMaxFeePerGas,\n estimates,\n );\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds,\n gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,\n };\n}\n\n/**\n * Retrieve gas fee estimates using the legacy endpoint of the gas API.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingLegacyEndpoint(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const {\n fetchLegacyGasPriceEstimates,\n fetchLegacyGasPriceEstimatesUrl,\n clientId,\n } = request;\n\n const estimates = await fetchLegacyGasPriceEstimates(\n fetchLegacyGasPriceEstimatesUrl,\n clientId,\n );\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY,\n };\n}\n\n/**\n * Retrieve gas fee estimates using an `eth_gasPrice` call to the RPC provider.\n * @param request - The request object.\n * @returns The gas fee estimates.\n */\nasync function getEstimatesUsingProvider(\n request: DetermineGasFeeCalculationsRequest,\n): Promise {\n const { ethQuery, fetchEthGasPriceEstimate } = request;\n\n const estimates = await fetchEthGasPriceEstimate(ethQuery);\n\n return {\n gasFeeEstimates: estimates,\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE,\n };\n}\n"]} -\ No newline at end of file -diff --git a/dist/determineGasFeeCalculations.js b/dist/determineGasFeeCalculations.js -index 87f583d091e574fc81b84ce35ee4b9979dbabdf6..ccfd460054f0affdec3857d8ec0015fe5c0814cb 100644 ---- a/dist/determineGasFeeCalculations.js -+++ b/dist/determineGasFeeCalculations.js -@@ -1,8 +1,8 @@ - "use strict";Object.defineProperty(exports, "__esModule", {value: true}); - --var _chunkH5WHAYLIjs = require('./chunk-H5WHAYLI.js'); --require('./chunk-Q2YPK5SL.js'); -+var _chunkX74LQX2Yjs = require('./chunk-X74LQX2Y.js'); -+require('./chunk-2MFVV2BX.js'); - - --exports.default = _chunkH5WHAYLIjs.determineGasFeeCalculations; -+exports.default = _chunkX74LQX2Yjs.determineGasFeeCalculations; - //# sourceMappingURL=determineGasFeeCalculations.js.map -\ No newline at end of file -diff --git a/dist/determineGasFeeCalculations.mjs b/dist/determineGasFeeCalculations.mjs -index b372041262f1d8372632922919f52aa2c6c5ee89..e5b349a8f349076b85481dceec5795b07aeb19ca 100644 ---- a/dist/determineGasFeeCalculations.mjs -+++ b/dist/determineGasFeeCalculations.mjs -@@ -1,7 +1,7 @@ - import { - determineGasFeeCalculations --} from "./chunk-BEVZS3YV.mjs"; --import "./chunk-KORLXV32.mjs"; -+} from "./chunk-A7NHJBXX.mjs"; -+import "./chunk-R3IOI7AK.mjs"; - export { - determineGasFeeCalculations as default - }; -diff --git a/dist/gas-util.js b/dist/gas-util.js -index 74c93749878df29b40eea3d998d26d734112d75c..aad155e1a969ac01a09f8e18becac39a79860199 100644 ---- a/dist/gas-util.js -+++ b/dist/gas-util.js -@@ -4,12 +4,12 @@ - - - --var _chunkQ2YPK5SLjs = require('./chunk-Q2YPK5SL.js'); -+var _chunk2MFVV2BXjs = require('./chunk-2MFVV2BX.js'); - - - - - - --exports.calculateTimeEstimate = _chunkQ2YPK5SLjs.calculateTimeEstimate; exports.fetchEthGasPriceEstimate = _chunkQ2YPK5SLjs.fetchEthGasPriceEstimate; exports.fetchGasEstimates = _chunkQ2YPK5SLjs.fetchGasEstimates; exports.fetchLegacyGasPriceEstimates = _chunkQ2YPK5SLjs.fetchLegacyGasPriceEstimates; exports.normalizeGWEIDecimalNumbers = _chunkQ2YPK5SLjs.normalizeGWEIDecimalNumbers; -+exports.calculateTimeEstimate = _chunk2MFVV2BXjs.calculateTimeEstimate; exports.fetchEthGasPriceEstimate = _chunk2MFVV2BXjs.fetchEthGasPriceEstimate; exports.fetchGasEstimates = _chunk2MFVV2BXjs.fetchGasEstimates; exports.fetchLegacyGasPriceEstimates = _chunk2MFVV2BXjs.fetchLegacyGasPriceEstimates; exports.normalizeGWEIDecimalNumbers = _chunk2MFVV2BXjs.normalizeGWEIDecimalNumbers; - //# sourceMappingURL=gas-util.js.map -\ No newline at end of file -diff --git a/dist/gas-util.mjs b/dist/gas-util.mjs -index c0846d32a7fe2598691c5c0bcc19ce2b8af67bc3..60f38a1b3899f0223fa7651988e57fa659e713e8 100644 ---- a/dist/gas-util.mjs -+++ b/dist/gas-util.mjs -@@ -4,7 +4,7 @@ import { - fetchGasEstimates, - fetchLegacyGasPriceEstimates, - normalizeGWEIDecimalNumbers --} from "./chunk-KORLXV32.mjs"; -+} from "./chunk-R3IOI7AK.mjs"; - export { - calculateTimeEstimate, - fetchEthGasPriceEstimate, -diff --git a/dist/index.js b/dist/index.js -index 499a7d57b68212db4402be72ab8d3384a841dba0..f172d463bef0093dd477dfad74ff50d30e42a7f0 100644 ---- a/dist/index.js -+++ b/dist/index.js -@@ -2,11 +2,11 @@ - - - --var _chunkH5WHAYLIjs = require('./chunk-H5WHAYLI.js'); --require('./chunk-Q2YPK5SL.js'); -+var _chunkX74LQX2Yjs = require('./chunk-X74LQX2Y.js'); -+require('./chunk-2MFVV2BX.js'); - - - - --exports.GAS_API_BASE_URL = _chunkH5WHAYLIjs.GAS_API_BASE_URL; exports.GAS_ESTIMATE_TYPES = _chunkH5WHAYLIjs.GAS_ESTIMATE_TYPES; exports.GasFeeController = _chunkH5WHAYLIjs.GasFeeController; -+exports.GAS_ESTIMATE_TYPES = _chunkX74LQX2Yjs.GAS_ESTIMATE_TYPES; exports.GasFeeController = _chunkX74LQX2Yjs.GasFeeController; exports.LEGACY_GAS_PRICES_API_URL = _chunkX74LQX2Yjs.LEGACY_GAS_PRICES_API_URL; - //# sourceMappingURL=index.js.map -\ No newline at end of file -diff --git a/dist/index.mjs b/dist/index.mjs -index 47fbe488932996ec192dd04a6b92d72c038681cc..71847186d44947ec3b2d75d142afbeede0aa64fc 100644 ---- a/dist/index.mjs -+++ b/dist/index.mjs -@@ -1,12 +1,12 @@ - import { -- GAS_API_BASE_URL, - GAS_ESTIMATE_TYPES, -- GasFeeController --} from "./chunk-BEVZS3YV.mjs"; --import "./chunk-KORLXV32.mjs"; -+ GasFeeController, -+ LEGACY_GAS_PRICES_API_URL -+} from "./chunk-A7NHJBXX.mjs"; -+import "./chunk-R3IOI7AK.mjs"; - export { -- GAS_API_BASE_URL, - GAS_ESTIMATE_TYPES, -- GasFeeController -+ GasFeeController, -+ LEGACY_GAS_PRICES_API_URL - }; - //# sourceMappingURL=index.mjs.map -\ No newline at end of file -diff --git a/dist/tsconfig.build.tsbuildinfo b/dist/tsconfig.build.tsbuildinfo -index c226cf271a32a00bf6054fba381ed5f846ed9752..008d925dcefc659662cbfa15bd507ce773b8fc56 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/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/@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/dist/cjs/index.d.ts","../../json-rpc-engine/dist/types/JsonRpcEngine.d.ts","../../json-rpc-engine/dist/types/createAsyncMiddleware.d.ts","../../json-rpc-engine/dist/types/createScaffoldMiddleware.d.ts","../../json-rpc-engine/dist/types/getUniqueId.d.ts","../../json-rpc-engine/dist/types/idRemapMiddleware.d.ts","../../json-rpc-engine/dist/types/mergeMiddleware.d.ts","../../json-rpc-engine/dist/types/index.d.ts","../../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","../../polling-controller/dist/types/types.d.ts","../../polling-controller/dist/types/BlockTrackerPollingController.d.ts","../../polling-controller/dist/types/StaticIntervalPollingController.d.ts","../../polling-controller/dist/types/index.d.ts","../../../node_modules/@types/uuid/index.d.ts","../src/determineGasFeeCalculations.ts","../src/gas-util.ts","../src/GasFeeController.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/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","5615ccf831db2ffc82145243081ebdb60ea8e1005ee8f975d1c0c1401a9c894e","38682ed3630bb6ecdace80d5a9adc811fc20a419f1940446e306c3a020d083b9","cc182e6e4f691cd6f7bf7cb491247a4c7818f9f1cb2db1d45c65ff906e3f741b","a50599c08934a62f11657bdbe0dc929ab66da1b1f09974408fd9a33ec1bb8060","5a20e7d6c630b91be15e9b837853173829d00273197481dc8d3e94df61105a71","8d478048d71cc16f806d4b71b252ecb67c7444ccf4f4b09b29a312712184f859","e0eda929c6b9b628cdeb0e54cd3582cb97e64f28aab34612fc1431c545899584","9df4662ca3dbc2522bc115833ee04faa1afbb4e249a85ef4a0a09c621346bd08","b25d9065cf1c1f537a140bbc508e953ed2262f77134574c432d206ff36f4bdbf","1b103313097041aa9cd705a682c652f08613cb5cf8663321061c0902f845e81c","68ccec8662818911d8a12b8ed028bc5729fb4f1d34793c4701265ba60bc73cf4","5f85b8b79dc4d36af672c035b2beb71545de63a5d60bccbeee64c260941672ab","b3d48529ae61dc27d0bfbfa2cb3e0dff8189644bd155bdf5df1e8e14669f7043","40fe4b689225816b31fe5794c0fbf3534568819709e40295ead998a2bc1ab237","f65b5e33b9ad545a1eebbd6afe857314725ad42aaf069913e33f928ab3e4990a","fb6f2a87beb7fb1f4c2b762d0c76a9459fc91f557231569b0ee21399e22aa13d","31c858dc85996fac4b7fa944e1016d5c72f514930a72357ab5001097bf6511c7","3de30a871b3340be8b679c52aa12f90dd1c8c60874517be58968fdbcc4d79445","6fd985bd31eaf77542625306fb0404d32bff978990f0a06428e5f0b9a3b58109","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","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","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","03460a54d0e0481d1e11097f66ad43f054bc95efdafe5f81bbc7a82be181af75","4070c2f1c3434fcf84886e04d30d82cd650ee443e53b82b404b144175cf8741e","2cea9689efa8591732096235abe7f084fc29c92badd5b0897a5e876b77e71887","4ed4e504126014fee13aaef5e3fc140f2ff7031ff3a8b5386717905820ea2d09","8129a34006218a6f3cdc81bbd438d5429eb18b08b4338a26977ac3b4df129d75","30d2170e1a718b5035611af55e3618b4ba8f42f0749bb52ee593da6082c4e2ce","98ef38666d88ec9699a722053e07ede65d3042f693fe7ff8c786e53dbb6fd43b","a3b8b6be7620897d1e481e8650c980a210a138fceb6e710eaf95fd9dd0dfe94a","12c89d0e32758c120a569045f21cf5b77244f86792611ced8de7f86b37e77781","14bd47270e654c8eb3b1489fa8c095912ee62a0a29bb92743393203722347c53","3d9297165e67fd59d9821cc93a9808213e33c56a8ac1c4273171f6afaaa2d4d5","e7af7d288b89287ad031b19583c597fcd9f5edc0b0d579b7b492f06cf57e058c","92cb686a9ca5eb5dd7d5d8d43a3707194c1e91ea07a027b3bcb60b6011b24632","fab58e600970e66547644a44bc9918e3223aa2cbd9e8763cec004b2cfb48827e",{"version":"f3e418819a6765dc6715ab2a51771c798543815d8499e29a857229bbf74ff419","signature":"b146c2211fb74ff8f672aa026d3992562e1c9c0d282cc712708b740c8f4cfb1a"},{"version":"d320adcc9e016b24e5873a7efb081d684a7990fbe23fe49c1c5354ba89bd129b","signature":"56c727d4c937865b6e7b114f58aa30f7678d5a9d924c0adb42368c403efb337d"},{"version":"fd1051fabcbc5740a5b3db8582c0bfee17883217121619fd765fbdd171f5d068","signature":"e4a433dbb39db571df95fa9677c8dede9cc6e21eb91a5a23e89ce2725cbd97da"},"d021f18758b28bda32bdaf0a987e0804cec074a9a4cfab8232ed81d96e75dfae","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","5e379df3d61561c2ed7789b5995b9ba2143bbba21a905e2381e16efe7d1fa424","f07a137bbe2de7a122c37bfea00e761975fb264c49f18003d398d71b3fb35a5f","d8f7109e14f20eb735225a62fd3f8366da1a8349e90331cdad57f4b04caf6c5a","cf3d384d082b933d987c4e2fe7bfb8710adfd9dc8155190056ed6695a25a559e","9871b7ee672bc16c78833bdab3052615834b08375cb144e4d2cba74473f4a589","c863198dae89420f3c552b5a03da6ed6d0acfa3807a64772b895db624b0de707","8b03a5e327d7db67112ebbc93b4f744133eda2c1743dbb0a990c61a8007823ef","86c73f2ee1752bac8eeeece234fd05dfcf0637a4fbd8032e4f5f43102faa8eec","42fad1f540271e35ca37cecda12c4ce2eef27f0f5cf0f8dd761d723c744d3159","ff3743a5de32bee10906aff63d1de726f6a7fd6ee2da4b8229054dfa69de2c34","83acd370f7f84f203e71ebba33ba61b7f1291ca027d7f9a662c6307d74e4ac22","1445cec898f90bdd18b2949b9590b3c012f5b7e1804e6e329fb0fe053946d5ec","0e5318ec2275d8da858b541920d9306650ae6ac8012f0e872fe66eb50321a669","cf530297c3fb3a92ec9591dd4fa229d58b5981e45fe6702a0bd2bea53a5e59be","c1f6f7d08d42148ddfe164d36d7aba91f467dbcb3caa715966ff95f55048b3a4","f4e9bf9103191ef3b3612d3ec0044ca4044ca5be27711fe648ada06fad4bcc85","0c1ee27b8f6a00097c2d6d91a21ee4d096ab52c1e28350f6362542b55380059a","7677d5b0db9e020d3017720f853ba18f415219fb3a9597343b1b1012cfd699f7","bc1c6bc119c1784b1a2be6d9c47addec0d83ef0d52c8fbe1f14a51b4dfffc675","52cf2ce99c2a23de70225e252e9822a22b4e0adb82643ab0b710858810e00bf1","770625067bb27a20b9826255a8d47b6b5b0a2d3dfcbd21f89904c731f671ba77","d1ed6765f4d7906a05968fb5cd6d1db8afa14dbe512a4884e8ea5c0f5e142c80","799c0f1b07c092626cf1efd71d459997635911bb5f7fc1196efe449bba87e965","2a184e4462b9914a30b1b5c41cf80c6d3428f17b20d3afb711fff3f0644001fd","9eabde32a3aa5d80de34af2c2206cdc3ee094c6504a8d0c2d6d20c7c179503cc","397c8051b6cfcb48aa22656f0faca2553c5f56187262135162ee79d2b2f6c966","a8ead142e0c87dcd5dc130eba1f8eeed506b08952d905c47621dc2f583b1bff9","a02f10ea5f73130efca046429254a4e3c06b5475baecc8f7b99a0014731be8b3","c2576a4083232b0e2d9bd06875dd43d371dee2e090325a9eac0133fd5650c1cb","4c9a0564bb317349de6a24eb4efea8bb79898fa72ad63a1809165f5bd42970dd","f40ac11d8859092d20f953aae14ba967282c3bb056431a37fced1866ec7a2681","cc11e9e79d4746cc59e0e17473a59d6f104692fd0eeea1bdb2e206eabed83b03","b444a410d34fb5e98aa5ee2b381362044f4884652e8bc8a11c8fe14bbd85518e","c35808c1f5e16d2c571aa65067e3cb95afeff843b259ecfa2fc107a9519b5392","14d5dc055143e941c8743c6a21fa459f961cbc3deedf1bfe47b11587ca4b3ef5","a3ad4e1fc542751005267d50a6298e6765928c0c3a8dce1572f2ba6ca518661c","f237e7c97a3a89f4591afd49ecb3bd8d14f51a1c4adc8fcae3430febedff5eb6","3ffdfbec93b7aed71082af62b8c3e0cc71261cc68d796665faa1e91604fbae8f","662201f943ed45b1ad600d03a90dffe20841e725203ced8b708c91fcd7f9379a","c9ef74c64ed051ea5b958621e7fb853fe3b56e8787c1587aefc6ea988b3c7e79","2462ccfac5f3375794b861abaa81da380f1bbd9401de59ffa43119a0b644253d","34baf65cfee92f110d6653322e2120c2d368ee64b3c7981dff08ed105c4f19b0","7d8ddf0f021c53099e34ee831a06c394d50371816caa98684812f089b4c6b3d4","7d2a0ba1297be385a89b5515b88cd31b4a1eeef5236f710166dc1b36b1741e1b","9d92b037978bb9525bc4b673ebddd443277542e010c0aef019c03a170ccdaa73","ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","ae271d475b632ce7b03fea6d9cf6da72439e57a109672671cbc79f54e1386938"],"options":{"composite":true,"declaration":true,"declarationMap":true,"emitDeclarationOnly":true,"esModuleInterop":true,"inlineSources":true,"module":1,"outDir":"./types","rootDir":"../src","sourceMap":true,"strict":true,"target":7},"fileIdsList":[[234],[92,128,129,130,145],[129,130,146,147],[128,129],[128,145,148,151],[128,148,151,152],[149,150,151,153,154],[128,151],[128,145,148,149,150,153],[128,136],[128],[92,128],[80,128],[132,133,134,135,136,137,138,139,140,141,142,143,144],[128,134,135],[128,134,136],[199],[199,200,201],[64],[67],[64,67],[65,66,67,68,69,70,71,72,73,74,75,156,159,160,161,162,163,164,165,166],[58,64,65],[67,73,75,155],[158],[67,68],[64,162],[194,195],[234,235,236,237,238],[234,236],[157],[241,242,243],[93,128],[246],[247],[258],[252,257],[261,263,264,265,266,267,268,269,270,271,272,273],[261,262,264,265,266,267,268,269,270,271,272,273],[262,263,264,265,266,267,268,269,270,271,272,273],[261,262,263,265,266,267,268,269,270,271,272,273],[261,262,263,264,266,267,268,269,270,271,272,273],[261,262,263,264,265,267,268,269,270,271,272,273],[261,262,263,264,265,266,268,269,270,271,272,273],[261,262,263,264,265,266,267,269,270,271,272,273],[261,262,263,264,265,266,267,268,270,271,272,273],[261,262,263,264,265,266,267,268,269,271,272,273],[261,262,263,264,265,266,267,268,269,270,272,273],[261,262,263,264,265,266,267,268,269,270,271,273],[261,262,263,264,265,266,267,268,269,270,271,272],[76],[79],[80,85,112],[81,92,93,100,109,120],[81,82,92,100],[83,121],[84,85,93,101],[85,109,117],[86,88,92,100],[87],[88,89],[92],[91,92],[79,92],[92,93,94,109,120],[92,93,94,109],[92,95,100,109,120],[92,93,95,96,100,109,117,120],[95,97,109,117,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,127],[92,98],[99,120,125],[88,92,100,109],[101],[102],[79,103],[104,119,125],[105],[106],[92,107],[107,108,121,123],[80,92,109,110,111],[80,109,111],[109,110],[112],[113],[92,115,116],[115,116],[85,100,109,117],[118],[100,119],[80,95,106,120],[85,121],[109,122],[123],[124],[80,85,92,94,103,109,120,123,125],[109,126],[128,279],[282,321],[282,306,321],[321],[282],[282,307,321],[282,283,284,285,286,287,288,289,290,291,292,293,294,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],[307,321],[322],[325],[204],[204,215,216],[216,217,218],[179],[179,180,181,182,183],[168,169,170,171,172,173,174,175,176,177,178],[250,253],[250,253,254,255],[252],[249,256],[251],[57,59,60,61,62,63],[57,58],[59],[58,59],[57,59],[167,184,185,186],[185],[186],[56,185,186,187],[189],[189,190,193,197],[196],[167,191,192],[212,213,214],[211,212],[167,211,212],[167,204,211],[167,188,191,198,224,228,229,230,231],[232],[191,192,198,232],[167,204],[167,205],[205,206,207,208,209,210],[167,188,191,198,202,203,220,221],[220],[203,220,222,223],[167,198,215,219],[167,188,224,225],[128,167,188,224,225],[225,226,227],[167,224],[167,188,224,228],[191,232]],"referencedMap":[[236,1],[146,2],[148,3],[130,4],[152,5],[153,6],[149,6],[155,7],[150,6],[154,8],[151,9],[137,10],[134,11],[141,12],[135,10],[132,13],[145,14],[139,11],[136,15],[138,16],[204,12],[200,17],[201,17],[202,18],[65,19],[66,19],[68,20],[69,19],[70,19],[71,21],[67,19],[167,22],[75,23],[156,24],[159,25],[165,26],[166,27],[196,28],[239,29],[235,1],[237,30],[238,1],[192,11],[158,31],[244,32],[245,33],[247,34],[248,35],[259,36],[258,37],[262,38],[263,39],[261,40],[264,41],[265,42],[266,43],[267,44],[268,45],[269,46],[270,47],[271,48],[272,49],[273,50],[76,51],[77,51],[79,52],[80,53],[81,54],[82,55],[83,56],[84,57],[85,58],[86,59],[87,60],[88,61],[89,61],[90,62],[91,63],[92,64],[93,65],[94,66],[95,67],[96,68],[97,69],[128,70],[98,71],[99,72],[100,73],[101,74],[102,75],[103,76],[104,77],[105,78],[106,79],[107,80],[108,81],[109,82],[111,83],[110,84],[112,85],[113,86],[115,87],[116,88],[117,89],[118,90],[119,91],[120,92],[121,93],[122,94],[123,95],[124,96],[125,97],[126,98],[276,11],[280,99],[281,11],[306,100],[307,101],[282,102],[285,102],[304,100],[305,100],[295,100],[294,103],[292,100],[287,100],[300,100],[298,100],[302,100],[286,100],[299,100],[303,100],[288,100],[289,100],[301,100],[283,100],[290,100],[291,100],[293,100],[297,100],[308,104],[296,100],[284,100],[321,105],[315,104],[317,106],[316,104],[309,104],[310,104],[312,104],[314,104],[318,106],[319,106],[311,106],[313,106],[323,107],[326,108],[216,109],[217,110],[218,110],[219,111],[175,112],[177,112],[176,112],[174,112],[184,113],[179,114],[170,112],[171,112],[172,112],[173,112],[254,115],[256,116],[255,115],[253,117],[257,118],[252,119],[64,120],[59,121],[60,122],[61,122],[62,123],[63,123],[58,124],[187,125],[186,126],[185,127],[188,128],[190,129],[198,130],[197,131],[193,132],[215,133],[213,134],[214,135],[212,136],[232,137],[230,138],[231,139],[233,138],[205,140],[206,141],[207,141],[209,141],[211,142],[210,141],[222,143],[221,144],[223,144],[224,145],[220,146],[226,147],[227,148],[228,149],[225,150]],"exportedModulesMap":[[236,1],[146,2],[148,3],[130,4],[152,5],[153,6],[149,6],[155,7],[150,6],[154,8],[151,9],[137,10],[134,11],[141,12],[135,10],[132,13],[145,14],[139,11],[136,15],[138,16],[204,12],[200,17],[201,17],[202,18],[65,19],[66,19],[68,20],[69,19],[70,19],[71,21],[67,19],[167,22],[75,23],[156,24],[159,25],[165,26],[166,27],[196,28],[239,29],[235,1],[237,30],[238,1],[192,11],[158,31],[244,32],[245,33],[247,34],[248,35],[259,36],[258,37],[262,38],[263,39],[261,40],[264,41],[265,42],[266,43],[267,44],[268,45],[269,46],[270,47],[271,48],[272,49],[273,50],[76,51],[77,51],[79,52],[80,53],[81,54],[82,55],[83,56],[84,57],[85,58],[86,59],[87,60],[88,61],[89,61],[90,62],[91,63],[92,64],[93,65],[94,66],[95,67],[96,68],[97,69],[128,70],[98,71],[99,72],[100,73],[101,74],[102,75],[103,76],[104,77],[105,78],[106,79],[107,80],[108,81],[109,82],[111,83],[110,84],[112,85],[113,86],[115,87],[116,88],[117,89],[118,90],[119,91],[120,92],[121,93],[122,94],[123,95],[124,96],[125,97],[126,98],[276,11],[280,99],[281,11],[306,100],[307,101],[282,102],[285,102],[304,100],[305,100],[295,100],[294,103],[292,100],[287,100],[300,100],[298,100],[302,100],[286,100],[299,100],[303,100],[288,100],[289,100],[301,100],[283,100],[290,100],[291,100],[293,100],[297,100],[308,104],[296,100],[284,100],[321,105],[315,104],[317,106],[316,104],[309,104],[310,104],[312,104],[314,104],[318,106],[319,106],[311,106],[313,106],[323,107],[326,108],[216,109],[217,110],[218,110],[219,111],[175,112],[177,112],[176,112],[174,112],[184,113],[179,114],[170,112],[171,112],[172,112],[173,112],[254,115],[256,116],[255,115],[253,117],[257,118],[252,119],[64,120],[59,121],[60,122],[61,122],[62,123],[63,123],[58,124],[187,125],[186,126],[185,127],[188,128],[190,129],[198,130],[197,131],[193,132],[215,133],[213,134],[214,135],[212,136],[232,151],[230,138],[231,152],[233,138],[205,140],[206,141],[207,141],[209,141],[211,142],[210,141],[222,143],[221,144],[223,144],[224,145],[220,146],[226,147],[227,148],[228,149],[225,150]],"semanticDiagnosticsPerFile":[236,234,146,129,148,130,147,152,153,149,155,150,154,151,137,134,141,135,132,140,145,142,143,144,139,136,133,138,191,204,200,201,202,199,65,66,68,69,70,71,72,73,74,67,167,75,156,159,160,161,162,163,164,165,166,194,196,195,239,235,237,238,192,158,240,241,244,242,245,246,247,248,259,258,243,260,262,263,261,264,265,266,267,268,269,270,271,272,273,274,157,76,77,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,78,127,95,96,97,128,98,99,100,101,102,103,104,105,106,107,108,109,111,110,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,275,276,277,278,280,279,281,306,307,282,285,304,305,295,294,292,287,300,298,302,286,299,303,288,289,301,283,290,291,293,297,308,296,284,321,320,315,317,316,309,310,312,314,318,319,311,313,323,322,324,229,325,326,131,249,216,217,218,219,178,175,177,176,174,184,179,183,180,182,181,170,171,172,168,169,173,250,254,256,255,253,257,252,251,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,56,187,186,185,188,190,198,197,189,193,215,213,214,212,232,230,231,233,205,206,207,208,209,211,210,222,203,221,223,224,220,226,227,228,225,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/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/@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/dist/cjs/index.d.ts","../../json-rpc-engine/dist/types/jsonrpcengine.d.ts","../../json-rpc-engine/dist/types/createasyncmiddleware.d.ts","../../json-rpc-engine/dist/types/createscaffoldmiddleware.d.ts","../../json-rpc-engine/dist/types/getuniqueid.d.ts","../../json-rpc-engine/dist/types/idremapmiddleware.d.ts","../../json-rpc-engine/dist/types/mergemiddleware.d.ts","../../json-rpc-engine/dist/types/index.d.ts","../../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","../../polling-controller/dist/types/types.d.ts","../../polling-controller/dist/types/blocktrackerpollingcontroller.d.ts","../../polling-controller/dist/types/staticintervalpollingcontroller.d.ts","../../polling-controller/dist/types/index.d.ts","../../../node_modules/@types/uuid/index.d.ts","../src/determinegasfeecalculations.ts","../src/gas-util.ts","../src/gasfeecontroller.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/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","5615ccf831db2ffc82145243081ebdb60ea8e1005ee8f975d1c0c1401a9c894e","38682ed3630bb6ecdace80d5a9adc811fc20a419f1940446e306c3a020d083b9","cc182e6e4f691cd6f7bf7cb491247a4c7818f9f1cb2db1d45c65ff906e3f741b","a50599c08934a62f11657bdbe0dc929ab66da1b1f09974408fd9a33ec1bb8060","5a20e7d6c630b91be15e9b837853173829d00273197481dc8d3e94df61105a71","8d478048d71cc16f806d4b71b252ecb67c7444ccf4f4b09b29a312712184f859","e0eda929c6b9b628cdeb0e54cd3582cb97e64f28aab34612fc1431c545899584","9df4662ca3dbc2522bc115833ee04faa1afbb4e249a85ef4a0a09c621346bd08","b25d9065cf1c1f537a140bbc508e953ed2262f77134574c432d206ff36f4bdbf","1b103313097041aa9cd705a682c652f08613cb5cf8663321061c0902f845e81c","68ccec8662818911d8a12b8ed028bc5729fb4f1d34793c4701265ba60bc73cf4","5f85b8b79dc4d36af672c035b2beb71545de63a5d60bccbeee64c260941672ab","b3d48529ae61dc27d0bfbfa2cb3e0dff8189644bd155bdf5df1e8e14669f7043","40fe4b689225816b31fe5794c0fbf3534568819709e40295ead998a2bc1ab237","f65b5e33b9ad545a1eebbd6afe857314725ad42aaf069913e33f928ab3e4990a","fb6f2a87beb7fb1f4c2b762d0c76a9459fc91f557231569b0ee21399e22aa13d","31c858dc85996fac4b7fa944e1016d5c72f514930a72357ab5001097bf6511c7","3de30a871b3340be8b679c52aa12f90dd1c8c60874517be58968fdbcc4d79445","6fd985bd31eaf77542625306fb0404d32bff978990f0a06428e5f0b9a3b58109","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","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","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","03460a54d0e0481d1e11097f66ad43f054bc95efdafe5f81bbc7a82be181af75","4070c2f1c3434fcf84886e04d30d82cd650ee443e53b82b404b144175cf8741e","2cea9689efa8591732096235abe7f084fc29c92badd5b0897a5e876b77e71887","4ed4e504126014fee13aaef5e3fc140f2ff7031ff3a8b5386717905820ea2d09","8129a34006218a6f3cdc81bbd438d5429eb18b08b4338a26977ac3b4df129d75","30d2170e1a718b5035611af55e3618b4ba8f42f0749bb52ee593da6082c4e2ce","98ef38666d88ec9699a722053e07ede65d3042f693fe7ff8c786e53dbb6fd43b","a3b8b6be7620897d1e481e8650c980a210a138fceb6e710eaf95fd9dd0dfe94a","12c89d0e32758c120a569045f21cf5b77244f86792611ced8de7f86b37e77781","14bd47270e654c8eb3b1489fa8c095912ee62a0a29bb92743393203722347c53","3d9297165e67fd59d9821cc93a9808213e33c56a8ac1c4273171f6afaaa2d4d5","e7af7d288b89287ad031b19583c597fcd9f5edc0b0d579b7b492f06cf57e058c","92cb686a9ca5eb5dd7d5d8d43a3707194c1e91ea07a027b3bcb60b6011b24632","fab58e600970e66547644a44bc9918e3223aa2cbd9e8763cec004b2cfb48827e",{"version":"d1a5f486914e3ead50534f48742c4e5e885d28909bec274017189d3284bcb833","signature":"b943b90c649678cec7987a6d56f4917db1712079294cffdf650699235ff9f37d"},{"version":"15433a2e97dcee760d97f8e56afe908fe932611ef00711aed9b246ba058ce0be","signature":"c5fc52cba80e6ba6fde33b0f9370b8ba6866831070cbc84b20a7c6e9e1163160"},{"version":"674d97345e4d79afd1248bdf3a032c9280f2ef163b1a074d14a1a927d93397a4","signature":"6991b558e53d59bb2160b64ee154678e069bd9bfe5131c628dcb6ac0363ca00d"},"d021f18758b28bda32bdaf0a987e0804cec074a9a4cfab8232ed81d96e75dfae","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","5e379df3d61561c2ed7789b5995b9ba2143bbba21a905e2381e16efe7d1fa424","f07a137bbe2de7a122c37bfea00e761975fb264c49f18003d398d71b3fb35a5f","d8f7109e14f20eb735225a62fd3f8366da1a8349e90331cdad57f4b04caf6c5a","cf3d384d082b933d987c4e2fe7bfb8710adfd9dc8155190056ed6695a25a559e","9871b7ee672bc16c78833bdab3052615834b08375cb144e4d2cba74473f4a589","c863198dae89420f3c552b5a03da6ed6d0acfa3807a64772b895db624b0de707","8b03a5e327d7db67112ebbc93b4f744133eda2c1743dbb0a990c61a8007823ef","86c73f2ee1752bac8eeeece234fd05dfcf0637a4fbd8032e4f5f43102faa8eec","42fad1f540271e35ca37cecda12c4ce2eef27f0f5cf0f8dd761d723c744d3159","ff3743a5de32bee10906aff63d1de726f6a7fd6ee2da4b8229054dfa69de2c34","83acd370f7f84f203e71ebba33ba61b7f1291ca027d7f9a662c6307d74e4ac22","1445cec898f90bdd18b2949b9590b3c012f5b7e1804e6e329fb0fe053946d5ec","0e5318ec2275d8da858b541920d9306650ae6ac8012f0e872fe66eb50321a669","cf530297c3fb3a92ec9591dd4fa229d58b5981e45fe6702a0bd2bea53a5e59be","c1f6f7d08d42148ddfe164d36d7aba91f467dbcb3caa715966ff95f55048b3a4","f4e9bf9103191ef3b3612d3ec0044ca4044ca5be27711fe648ada06fad4bcc85","0c1ee27b8f6a00097c2d6d91a21ee4d096ab52c1e28350f6362542b55380059a","7677d5b0db9e020d3017720f853ba18f415219fb3a9597343b1b1012cfd699f7","bc1c6bc119c1784b1a2be6d9c47addec0d83ef0d52c8fbe1f14a51b4dfffc675","52cf2ce99c2a23de70225e252e9822a22b4e0adb82643ab0b710858810e00bf1","770625067bb27a20b9826255a8d47b6b5b0a2d3dfcbd21f89904c731f671ba77","d1ed6765f4d7906a05968fb5cd6d1db8afa14dbe512a4884e8ea5c0f5e142c80","799c0f1b07c092626cf1efd71d459997635911bb5f7fc1196efe449bba87e965","2a184e4462b9914a30b1b5c41cf80c6d3428f17b20d3afb711fff3f0644001fd","9eabde32a3aa5d80de34af2c2206cdc3ee094c6504a8d0c2d6d20c7c179503cc","397c8051b6cfcb48aa22656f0faca2553c5f56187262135162ee79d2b2f6c966","a8ead142e0c87dcd5dc130eba1f8eeed506b08952d905c47621dc2f583b1bff9","a02f10ea5f73130efca046429254a4e3c06b5475baecc8f7b99a0014731be8b3","c2576a4083232b0e2d9bd06875dd43d371dee2e090325a9eac0133fd5650c1cb","4c9a0564bb317349de6a24eb4efea8bb79898fa72ad63a1809165f5bd42970dd","f40ac11d8859092d20f953aae14ba967282c3bb056431a37fced1866ec7a2681","cc11e9e79d4746cc59e0e17473a59d6f104692fd0eeea1bdb2e206eabed83b03","b444a410d34fb5e98aa5ee2b381362044f4884652e8bc8a11c8fe14bbd85518e","c35808c1f5e16d2c571aa65067e3cb95afeff843b259ecfa2fc107a9519b5392","14d5dc055143e941c8743c6a21fa459f961cbc3deedf1bfe47b11587ca4b3ef5","a3ad4e1fc542751005267d50a6298e6765928c0c3a8dce1572f2ba6ca518661c","f237e7c97a3a89f4591afd49ecb3bd8d14f51a1c4adc8fcae3430febedff5eb6","3ffdfbec93b7aed71082af62b8c3e0cc71261cc68d796665faa1e91604fbae8f","662201f943ed45b1ad600d03a90dffe20841e725203ced8b708c91fcd7f9379a","c9ef74c64ed051ea5b958621e7fb853fe3b56e8787c1587aefc6ea988b3c7e79","2462ccfac5f3375794b861abaa81da380f1bbd9401de59ffa43119a0b644253d","34baf65cfee92f110d6653322e2120c2d368ee64b3c7981dff08ed105c4f19b0","7d8ddf0f021c53099e34ee831a06c394d50371816caa98684812f089b4c6b3d4","7d2a0ba1297be385a89b5515b88cd31b4a1eeef5236f710166dc1b36b1741e1b","9d92b037978bb9525bc4b673ebddd443277542e010c0aef019c03a170ccdaa73","ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","ae271d475b632ce7b03fea6d9cf6da72439e57a109672671cbc79f54e1386938"],"options":{"composite":true,"declaration":true,"declarationMap":true,"emitDeclarationOnly":true,"esModuleInterop":true,"inlineSources":true,"module":1,"outDir":"./types","rootDir":"../src","sourceMap":true,"strict":true,"target":7},"fileIdsList":[[234],[92,128,129,130,145],[129,130,146,147],[128,129],[128,145,148,151],[128,148,151,152],[149,150,151,153,154],[128,151],[128,145,148,149,150,153],[128,136],[128],[92,128],[80,128],[132,133,134,135,136,137,138,139,140,141,142,143,144],[128,134,135],[128,134,136],[199],[199,200,201],[64],[67],[64,67],[65,66,67,68,69,70,71,72,73,74,75,156,159,160,161,162,163,164,165,166],[58,64,65],[67,73,75,155],[158],[67,68],[64,162],[194,195],[234,235,236,237,238],[234,236],[157],[241,242,243],[93,128],[246],[247],[258],[252,257],[261,263,264,265,266,267,268,269,270,271,272,273],[261,262,264,265,266,267,268,269,270,271,272,273],[262,263,264,265,266,267,268,269,270,271,272,273],[261,262,263,265,266,267,268,269,270,271,272,273],[261,262,263,264,266,267,268,269,270,271,272,273],[261,262,263,264,265,267,268,269,270,271,272,273],[261,262,263,264,265,266,268,269,270,271,272,273],[261,262,263,264,265,266,267,269,270,271,272,273],[261,262,263,264,265,266,267,268,270,271,272,273],[261,262,263,264,265,266,267,268,269,271,272,273],[261,262,263,264,265,266,267,268,269,270,272,273],[261,262,263,264,265,266,267,268,269,270,271,273],[261,262,263,264,265,266,267,268,269,270,271,272],[76],[79],[80,85,112],[81,92,93,100,109,120],[81,82,92,100],[83,121],[84,85,93,101],[85,109,117],[86,88,92,100],[87],[88,89],[92],[91,92],[79,92],[92,93,94,109,120],[92,93,94,109],[92,95,100,109,120],[92,93,95,96,100,109,117,120],[95,97,109,117,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,127],[92,98],[99,120,125],[88,92,100,109],[101],[102],[79,103],[104,119,125],[105],[106],[92,107],[107,108,121,123],[80,92,109,110,111],[80,109,111],[109,110],[112],[113],[92,115,116],[115,116],[85,100,109,117],[118],[100,119],[80,95,106,120],[85,121],[109,122],[123],[124],[80,85,92,94,103,109,120,123,125],[109,126],[128,279],[282,321],[282,306,321],[321],[282],[282,307,321],[282,283,284,285,286,287,288,289,290,291,292,293,294,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],[307,321],[322],[325],[204],[216,217,218],[204,215,216],[179],[179,180,181,182,183],[168,169,170,171,172,173,174,175,176,177,178],[250,253],[250,253,254,255],[252],[249,256],[251],[57,59,60,61,62,63],[57,58],[59],[58,59],[57,59],[167,184,185,186],[185],[56,185,186,187],[186],[189],[189,190,193,197],[196],[167,191,192],[212,213,214],[211,212],[167,211,212],[167,204,211],[232],[191,192,198,232],[167,188,191,198,224,228,229,230,231],[167,205],[205,206,207,208,209,210],[167,204],[220],[203,220,222,223],[167,188,191,198,202,203,220,221],[167,198,215,219],[167,188,224,225],[225,226,227],[128,167,188,224,225],[167,224],[191,232],[167,188,224,228]],"referencedMap":[[236,1],[146,2],[148,3],[130,4],[152,5],[153,6],[149,6],[155,7],[150,6],[154,8],[151,9],[137,10],[134,11],[141,12],[135,10],[132,13],[145,14],[139,11],[136,15],[138,16],[204,12],[200,17],[201,17],[202,18],[65,19],[66,19],[68,20],[69,19],[70,19],[71,21],[67,19],[167,22],[75,23],[156,24],[159,25],[165,26],[166,27],[196,28],[239,29],[235,1],[237,30],[238,1],[192,11],[158,31],[244,32],[245,33],[247,34],[248,35],[259,36],[258,37],[262,38],[263,39],[261,40],[264,41],[265,42],[266,43],[267,44],[268,45],[269,46],[270,47],[271,48],[272,49],[273,50],[76,51],[77,51],[79,52],[80,53],[81,54],[82,55],[83,56],[84,57],[85,58],[86,59],[87,60],[88,61],[89,61],[90,62],[91,63],[92,64],[93,65],[94,66],[95,67],[96,68],[97,69],[128,70],[98,71],[99,72],[100,73],[101,74],[102,75],[103,76],[104,77],[105,78],[106,79],[107,80],[108,81],[109,82],[111,83],[110,84],[112,85],[113,86],[115,87],[116,88],[117,89],[118,90],[119,91],[120,92],[121,93],[122,94],[123,95],[124,96],[125,97],[126,98],[276,11],[280,99],[281,11],[306,100],[307,101],[282,102],[285,102],[304,100],[305,100],[295,100],[294,103],[292,100],[287,100],[300,100],[298,100],[302,100],[286,100],[299,100],[303,100],[288,100],[289,100],[301,100],[283,100],[290,100],[291,100],[293,100],[297,100],[308,104],[296,100],[284,100],[321,105],[315,104],[317,106],[316,104],[309,104],[310,104],[312,104],[314,104],[318,106],[319,106],[311,106],[313,106],[323,107],[326,108],[216,109],[219,110],[217,111],[218,111],[175,112],[177,112],[176,112],[174,112],[184,113],[179,114],[170,112],[171,112],[172,112],[173,112],[254,115],[256,116],[255,115],[253,117],[257,118],[252,119],[64,120],[59,121],[60,122],[61,122],[62,123],[63,123],[58,124],[187,125],[186,126],[188,127],[185,128],[190,129],[198,130],[197,131],[193,132],[215,133],[213,134],[214,135],[212,136],[230,137],[231,138],[232,139],[233,137],[206,140],[207,140],[209,140],[211,141],[205,142],[210,140],[221,143],[223,143],[224,144],[222,145],[220,146],[226,147],[228,148],[227,149],[225,150]],"exportedModulesMap":[[236,1],[146,2],[148,3],[130,4],[152,5],[153,6],[149,6],[155,7],[150,6],[154,8],[151,9],[137,10],[134,11],[141,12],[135,10],[132,13],[145,14],[139,11],[136,15],[138,16],[204,12],[200,17],[201,17],[202,18],[65,19],[66,19],[68,20],[69,19],[70,19],[71,21],[67,19],[167,22],[75,23],[156,24],[159,25],[165,26],[166,27],[196,28],[239,29],[235,1],[237,30],[238,1],[192,11],[158,31],[244,32],[245,33],[247,34],[248,35],[259,36],[258,37],[262,38],[263,39],[261,40],[264,41],[265,42],[266,43],[267,44],[268,45],[269,46],[270,47],[271,48],[272,49],[273,50],[76,51],[77,51],[79,52],[80,53],[81,54],[82,55],[83,56],[84,57],[85,58],[86,59],[87,60],[88,61],[89,61],[90,62],[91,63],[92,64],[93,65],[94,66],[95,67],[96,68],[97,69],[128,70],[98,71],[99,72],[100,73],[101,74],[102,75],[103,76],[104,77],[105,78],[106,79],[107,80],[108,81],[109,82],[111,83],[110,84],[112,85],[113,86],[115,87],[116,88],[117,89],[118,90],[119,91],[120,92],[121,93],[122,94],[123,95],[124,96],[125,97],[126,98],[276,11],[280,99],[281,11],[306,100],[307,101],[282,102],[285,102],[304,100],[305,100],[295,100],[294,103],[292,100],[287,100],[300,100],[298,100],[302,100],[286,100],[299,100],[303,100],[288,100],[289,100],[301,100],[283,100],[290,100],[291,100],[293,100],[297,100],[308,104],[296,100],[284,100],[321,105],[315,104],[317,106],[316,104],[309,104],[310,104],[312,104],[314,104],[318,106],[319,106],[311,106],[313,106],[323,107],[326,108],[216,109],[219,110],[217,111],[218,111],[175,112],[177,112],[176,112],[174,112],[184,113],[179,114],[170,112],[171,112],[172,112],[173,112],[254,115],[256,116],[255,115],[253,117],[257,118],[252,119],[64,120],[59,121],[60,122],[61,122],[62,123],[63,123],[58,124],[187,125],[186,126],[188,127],[185,128],[190,129],[198,130],[197,131],[193,132],[215,133],[213,134],[214,135],[212,136],[230,137],[231,151],[232,152],[233,137],[206,140],[207,140],[209,140],[211,141],[205,142],[210,140],[221,143],[223,143],[224,144],[222,145],[220,146],[226,147],[228,148],[227,149],[225,150]],"semanticDiagnosticsPerFile":[236,234,146,129,148,130,147,152,153,149,155,150,154,151,137,134,141,135,132,140,145,142,143,144,139,136,133,138,191,204,200,201,202,199,65,66,68,69,70,71,72,73,74,67,167,75,156,159,160,161,162,163,164,165,166,194,196,195,239,235,237,238,192,158,240,241,244,242,245,246,247,248,259,258,243,260,262,263,261,264,265,266,267,268,269,270,271,272,273,274,157,76,77,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,78,127,95,96,97,128,98,99,100,101,102,103,104,105,106,107,108,109,111,110,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,275,276,277,278,280,279,281,306,307,282,285,304,305,295,294,292,287,300,298,302,286,299,303,288,289,301,283,290,291,293,297,308,296,284,321,320,315,317,316,309,310,312,314,318,319,311,313,323,322,324,229,325,326,131,249,216,219,217,218,178,175,177,176,174,184,179,183,180,182,181,170,171,172,168,169,173,250,254,256,255,253,257,252,251,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,56,187,186,188,185,190,198,197,189,193,215,213,214,212,230,231,232,233,206,207,208,209,211,205,210,203,221,223,224,222,220,226,228,227,225,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/GasFeeController.d.ts b/dist/types/GasFeeController.d.ts -index b6ffa7cd5b356ef6c5df6a79876308d21749bd06..56d9420b539cc8d98b8aeb0a84ba541fc3beae1b 100644 ---- a/dist/types/GasFeeController.d.ts -+++ b/dist/types/GasFeeController.d.ts -@@ -2,7 +2,7 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, RestrictedCo - import type { NetworkClientId, NetworkControllerGetEIP1559CompatibilityAction, NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction, NetworkControllerNetworkDidChangeEvent, NetworkState, ProviderProxy } from '@metamask/network-controller'; - import { StaticIntervalPollingController } from '@metamask/polling-controller'; - import type { Hex } from '@metamask/utils'; --export declare const GAS_API_BASE_URL = "https://gas.api.infura.io"; -+export declare const LEGACY_GAS_PRICES_API_URL = "https://api.metaswap.codefi.network/gasPrices"; - export type unknownString = 'unknown'; - export type FeeMarketEstimateType = 'fee-market'; - export type LegacyEstimateType = 'legacy'; -@@ -160,7 +160,6 @@ export declare class GasFeeController extends StaticIntervalPollingController Hex; - getProvider: () => ProviderProxy; - onNetworkDidChange?: (listener: (state: NetworkState) => void) => void; -+ legacyAPIEndpoint?: string; -+ EIP1559APIEndpoint: string; - clientId?: string; -- infuraAPIKey: string; - }); - resetPolling(): Promise; - fetchGasFeeEstimates(options?: FetchGasFeeEstimateOptions): Promise; -diff --git a/dist/types/GasFeeController.d.ts.map b/dist/types/GasFeeController.d.ts.map -index 0dd80816d0083029fcc5ef3a85a47f188a5b4c02..127cdbe0c0fbfb37938403190c49d2a5687cf9c4 100644 ---- a/dist/types/GasFeeController.d.ts.map -+++ b/dist/types/GasFeeController.d.ts.map -@@ -1 +1 @@ --{"version":3,"file":"GasFeeController.d.ts","sourceRoot":"","sources":["../../src/GasFeeController.ts"],"names":[],"mappings":"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"} -\ No newline at end of file -+{"version":3,"file":"GasFeeController.d.ts","sourceRoot":"","sources":["../../src/GasFeeController.ts"],"names":[],"mappings":"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"} -\ No newline at end of file -diff --git a/dist/types/determineGasFeeCalculations.d.ts b/dist/types/determineGasFeeCalculations.d.ts -index 8091a4d4290d6a3ca9ecbecc9ab8c6764a58cac9..6f4b2758a99e4d0d3fd2c8dce4064a2a2fc69c0a 100644 ---- a/dist/types/determineGasFeeCalculations.d.ts -+++ b/dist/types/determineGasFeeCalculations.d.ts -@@ -2,15 +2,14 @@ import type { EstimatedGasFeeTimeBounds, EthGasPriceEstimate, GasFeeEstimates, G - type DetermineGasFeeCalculationsRequest = { - isEIP1559Compatible: boolean; - isLegacyGasAPICompatible: boolean; -- fetchGasEstimates: (url: string, infuraAPIKey: string, clientId?: string) => Promise; -+ fetchGasEstimates: (url: string, clientId?: string) => Promise; - fetchGasEstimatesUrl: string; -- fetchLegacyGasPriceEstimates: (url: string, infuraAPIKey: string, clientId?: string) => Promise; -+ fetchLegacyGasPriceEstimates: (url: string, clientId?: string) => Promise; - fetchLegacyGasPriceEstimatesUrl: string; - fetchEthGasPriceEstimate: (ethQuery: any) => Promise; - calculateTimeEstimate: (maxPriorityFeePerGas: string, maxFeePerGas: string, gasFeeEstimates: GasFeeEstimates) => EstimatedGasFeeTimeBounds; - clientId: string | undefined; - ethQuery: any; -- infuraAPIKey: string; - nonRPCGasFeeApisDisabled?: boolean; - }; - /** -@@ -35,7 +34,6 @@ type DetermineGasFeeCalculationsRequest = { - * @param args.calculateTimeEstimate - A function that determine time estimate bounds. - * @param args.clientId - An identifier that an API can use to know who is asking for estimates. - * @param args.ethQuery - An EthQuery instance we can use to talk to Ethereum directly. -- * @param args.infuraAPIKey - Infura API key to use for requests to Infura. - * @param args.nonRPCGasFeeApisDisabled - Whether to disable requests to the legacyAPIEndpoint and the EIP1559APIEndpoint - * @returns The gas fee calculations. - */ -diff --git a/dist/types/determineGasFeeCalculations.d.ts.map b/dist/types/determineGasFeeCalculations.d.ts.map -index 44ce4dc7b691ea0316163952f99d426d417afd1a..17cec260fc0836f59929a053270164e5c10e4b89 100644 ---- a/dist/types/determineGasFeeCalculations.d.ts.map -+++ b/dist/types/determineGasFeeCalculations.d.ts.map -@@ -1 +1 @@ --{"version":3,"file":"determineGasFeeCalculations.d.ts","sourceRoot":"","sources":["../../src/determineGasFeeCalculations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,mBAAmB,EACnB,eAAe,EACf,WAAW,IAAI,kBAAkB,EACjC,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAG5B,KAAK,kCAAkC,GAAG;IACxC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,wBAAwB,EAAE,OAAO,CAAC;IAClC,iBAAiB,EAAE,CACjB,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,KACd,OAAO,CAAC,eAAe,CAAC,CAAC;IAC9B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,4BAA4B,EAAE,CAC5B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,KACd,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACrC,+BAA+B,EAAE,MAAM,CAAC;IAGxC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC1E,qBAAqB,EAAE,CACrB,oBAAoB,EAAE,MAAM,EAC5B,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,eAAe,KAC7B,yBAAyB,CAAC;IAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAG7B,QAAQ,EAAE,GAAG,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAA8B,2BAA2B,CACvD,IAAI,EAAE,kCAAkC,GACvC,OAAO,CAAC,kBAAkB,CAAC,CAY7B"} -\ No newline at end of file -+{"version":3,"file":"determineGasFeeCalculations.d.ts","sourceRoot":"","sources":["../../src/determineGasFeeCalculations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,mBAAmB,EACnB,eAAe,EACf,WAAW,IAAI,kBAAkB,EACjC,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAG5B,KAAK,kCAAkC,GAAG;IACxC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,wBAAwB,EAAE,OAAO,CAAC;IAClC,iBAAiB,EAAE,CACjB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,KACd,OAAO,CAAC,eAAe,CAAC,CAAC;IAC9B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,4BAA4B,EAAE,CAC5B,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,KACd,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACrC,+BAA+B,EAAE,MAAM,CAAC;IAGxC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC1E,qBAAqB,EAAE,CACrB,oBAAoB,EAAE,MAAM,EAC5B,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,eAAe,KAC7B,yBAAyB,CAAC;IAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAG7B,QAAQ,EAAE,GAAG,CAAC;IACd,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAA8B,2BAA2B,CACvD,IAAI,EAAE,kCAAkC,GACvC,OAAO,CAAC,kBAAkB,CAAC,CAY7B"} -\ No newline at end of file -diff --git a/dist/types/gas-util.d.ts b/dist/types/gas-util.d.ts -index 3739e0ffc0f895d20b5ab1e706b893e8606ef62b..661c3a87fbb2160075801e676d216809b01ecf6e 100644 ---- a/dist/types/gas-util.d.ts -+++ b/dist/types/gas-util.d.ts -@@ -11,21 +11,19 @@ export declare function normalizeGWEIDecimalNumbers(n: string | number): any; - * Fetch gas estimates from the given URL. - * - * @param url - The gas estimate URL. -- * @param infuraAPIKey - The Infura API key used for infura API requests. - * @param clientId - The client ID used to identify to the API who is asking for estimates. - * @returns The gas estimates. - */ --export declare function fetchGasEstimates(url: string, infuraAPIKey: string, clientId?: string): Promise; -+export declare function fetchGasEstimates(url: string, clientId?: string): Promise; - /** - * Hit the legacy MetaSwaps gasPrices estimate api and return the low, medium - * high values from that API. - * - * @param url - The URL to fetch gas price estimates from. -- * @param infuraAPIKey - The Infura API key used for infura API requests. - * @param clientId - The client ID used to identify to the API who is asking for estimates. - * @returns The gas price estimates. - */ --export declare function fetchLegacyGasPriceEstimates(url: string, infuraAPIKey: string, clientId?: string): Promise; -+export declare function fetchLegacyGasPriceEstimates(url: string, clientId?: string): Promise; - /** - * Get a gas price estimate from the network using the `eth_gasPrice` method. - * -diff --git a/dist/types/gas-util.d.ts.map b/dist/types/gas-util.d.ts.map -index 8a24af1980fc16f7ecbe97222cab534b3c0f6c2c..ab417991781db0ad097218682c264d7aa738bf12 100644 ---- a/dist/types/gas-util.d.ts.map -+++ b/dist/types/gas-util.d.ts.map -@@ -1 +1 @@ --{"version":3,"file":"gas-util.d.ts","sourceRoot":"","sources":["../../src/gas-util.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAGhD,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EAEzB,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAI5B;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,OAI7D;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,CAAC,CAyC1B;AAED;;;;;;;;GAQG;AACH,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,sBAAsB,CAAC,CAcjC;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,mBAAmB,CAAC,CAK9B;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,oBAAoB,EAAE,MAAM,EAC5B,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,eAAe,GAC/B,yBAAyB,CAoD3B"} -\ No newline at end of file -+{"version":3,"file":"gas-util.d.ts","sourceRoot":"","sources":["../../src/gas-util.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAGhD,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EAEzB,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAI5B;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,OAI7D;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,CAAC,CAyC1B;AAED;;;;;;;GAOG;AACH,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,sBAAsB,CAAC,CAgBjC;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,mBAAmB,CAAC,CAK9B;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,oBAAoB,EAAE,MAAM,EAC5B,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,eAAe,GAC/B,yBAAyB,CAoD3B"} -\ No newline at end of file diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index b04c11548697..8683dc1bb47d 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -278,7 +278,10 @@ "description": "$1 is Learn more link" }, "addNewAccount": { - "message": "Add a new account" + "message": "Add a new Ethereum account" + }, + "addNewBitcoinAccount": { + "message": "Add a new Bitcoin account (Beta)" }, "addNewToken": { "message": "Add new token" @@ -756,6 +759,15 @@ "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." + }, + "bitcoinSupportToggleTitle": { + "message": "Enable \"Add a new Bitcoin account (Beta)\"" + }, "blockExplorerAccountAction": { "message": "Account", "description": "This is used with viewOnEtherscan and viewInExplorer e.g View Account in Explorer" @@ -1974,6 +1986,9 @@ "forgotPassword": { "message": "Forgot password?" }, + "form": { + "message": "form" + }, "from": { "message": "From" }, @@ -5986,6 +6001,9 @@ "transactionCreated": { "message": "Transaction created with a value of $1 at $2." }, + "transactionDataFunction": { + "message": "Function" + }, "transactionDetailDappGasMoreInfo": { "message": "Site suggested" }, diff --git a/app/scripts/controllers/permissions/caveat-mutators.js b/app/scripts/controllers/permissions/caveat-mutators.js index d712a681e1e3..551d1f7b37b2 100644 --- a/app/scripts/controllers/permissions/caveat-mutators.js +++ b/app/scripts/controllers/permissions/caveat-mutators.js @@ -1,6 +1,6 @@ import { CaveatMutatorOperation } from '@metamask/permission-controller'; -import { toChecksumAddress } from 'ethereumjs-util'; import { CaveatTypes } from '../../../../shared/constants/permissions'; +import { normalizeSafeAddress } from '../../lib/multichain/address'; /** * Factories that construct caveat mutator functions that are passed to @@ -27,9 +27,9 @@ export const CaveatMutatorFactories = { * account permissions. */ function removeAccount(targetAccount, existingAccounts) { - const checkSumTargetAccount = toChecksumAddress(targetAccount); + const checkSumTargetAccount = normalizeSafeAddress(targetAccount); const newAccounts = existingAccounts.filter( - (address) => toChecksumAddress(address) !== checkSumTargetAccount, + (address) => normalizeSafeAddress(address) !== checkSumTargetAccount, ); if (newAccounts.length === existingAccounts.length) { diff --git a/app/scripts/controllers/permissions/caveat-mutators.test.js b/app/scripts/controllers/permissions/caveat-mutators.test.js index 43a4a55effe8..a87115dc744b 100644 --- a/app/scripts/controllers/permissions/caveat-mutators.test.js +++ b/app/scripts/controllers/permissions/caveat-mutators.test.js @@ -2,6 +2,10 @@ import { CaveatMutatorOperation } from '@metamask/permission-controller'; import { CaveatTypes } from '../../../../shared/constants/permissions'; import { CaveatMutatorFactories } from './caveat-mutators'; +const address1 = '0xbf16f7f5db8ae6af2512399bace3101debbde7fc'; +const address2 = '0xb6d5abeca51bfc3d53d00afed06b17eeea32ecdf'; +const nonEvmAddress = 'bc1qdkwac3em6mvlur4fatn2g4q050f4kkqadrsmnp'; + describe('caveat mutators', () => { describe('restrictReturnedAccounts', () => { const { removeAccount } = @@ -9,33 +13,55 @@ describe('caveat mutators', () => { describe('removeAccount', () => { it('returns the no-op operation if the target account is not permitted', () => { - expect(removeAccount('0x2', ['0x1'])).toStrictEqual({ + expect(removeAccount(address2, [address1])).toStrictEqual({ operation: CaveatMutatorOperation.noop, }); }); it('returns the update operation and a new value if the target account is permitted', () => { - expect(removeAccount('0x2', ['0x1', '0x2'])).toStrictEqual({ + expect(removeAccount(address2, [address1, address2])).toStrictEqual({ operation: CaveatMutatorOperation.updateValue, - value: ['0x1'], + value: [address1], }); }); it('returns the revoke permission operation the target account is the only permitted account', () => { - expect(removeAccount('0x1', ['0x1'])).toStrictEqual({ + expect(removeAccount(address1, [address1])).toStrictEqual({ operation: CaveatMutatorOperation.revokePermission, }); }); it('returns the revoke permission operation even if the target account is a checksummed address', () => { - expect( - removeAccount('0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAee5', [ - '0x95222290dd7278aa3ddd389cc1e1d165cc4baee5', - ]), - ).toStrictEqual({ + const address3 = '0x95222290dd7278aa3ddd389cc1e1d165cc4baee5'; + const checksummedAddress3 = + '0x95222290dd7278AA3DDd389cc1E1d165Cc4BaeE5'; + expect(removeAccount(checksummedAddress3, [address3])).toStrictEqual({ operation: CaveatMutatorOperation.revokePermission, }); }); + + describe('Multichain behaviour', () => { + it('returns the no-op operation if the target account is not permitted', () => { + expect(removeAccount(address2, [nonEvmAddress])).toStrictEqual({ + operation: CaveatMutatorOperation.noop, + }); + }); + + it('can revoke permission for non-EVM addresses', () => { + expect(removeAccount(nonEvmAddress, [nonEvmAddress])).toStrictEqual({ + operation: CaveatMutatorOperation.revokePermission, + }); + }); + + it('returns the update operation and a new value if the target non-EVM account is permitted', () => { + expect( + removeAccount(nonEvmAddress, [address1, nonEvmAddress]), + ).toStrictEqual({ + operation: CaveatMutatorOperation.updateValue, + value: [address1], + }); + }); + }); }); }); }); diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index cfa5907a5194..aa14bc84a1a4 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -61,6 +61,7 @@ export default class PreferencesController { useRequestQueue: true, openSeaEnabled: true, // todo set this to true securityAlertsEnabled: true, + bitcoinSupportEnabled: false, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) addSnapAccountEnabled: false, ///: END:ONLY_INCLUDE_IF @@ -290,6 +291,18 @@ export default class PreferencesController { } ///: END:ONLY_INCLUDE_IF + /** + * Setter for the `bitcoinSupportEnabled` property. + * + * @param {boolean} bitcoinSupportEnabled - Whether or not the user wants to + * enable the "Add a new Bitcoin account" button. + */ + setBitcoinSupportEnabled(bitcoinSupportEnabled) { + this.store.updateState({ + bitcoinSupportEnabled, + }); + } + /** * Setter for the `useExternalNameSources` property * diff --git a/app/scripts/controllers/preferences.test.js b/app/scripts/controllers/preferences.test.js index cd8e9d3fe896..e3737f685b7e 100644 --- a/app/scripts/controllers/preferences.test.js +++ b/app/scripts/controllers/preferences.test.js @@ -560,4 +560,24 @@ describe('preferences controller', () => { ).toStrictEqual(false); }); }); + + describe('setBitcoinSupportEnabled', () => { + it('has the default value as false', () => { + expect( + preferencesController.store.getState().bitcoinSupportEnabled, + ).toStrictEqual(false); + }); + + it('sets the bitcoinSupportEnabled property in state to true and then false', () => { + preferencesController.setBitcoinSupportEnabled(true); + expect( + preferencesController.store.getState().bitcoinSupportEnabled, + ).toStrictEqual(true); + + preferencesController.setBitcoinSupportEnabled(false); + expect( + preferencesController.store.getState().bitcoinSupportEnabled, + ).toStrictEqual(false); + }); + }); }); diff --git a/app/scripts/lib/ppom/types.ts b/app/scripts/lib/ppom/types.ts index 6532b7693236..6188d644aa12 100644 --- a/app/scripts/lib/ppom/types.ts +++ b/app/scripts/lib/ppom/types.ts @@ -1,6 +1,7 @@ import { SecurityAlertSource } from '../../../../shared/constants/security-provider'; export type SecurityAlertResponse = { + block?: number; description?: string; features?: string[]; providerRequestsCount?: Record; diff --git a/app/scripts/lib/setupSentry.js b/app/scripts/lib/setupSentry.js index 695b995ddaa8..2d0df8f78994 100644 --- a/app/scripts/lib/setupSentry.js +++ b/app/scripts/lib/setupSentry.js @@ -424,6 +424,7 @@ export const SENTRY_UI_STATE = { welcomeScreenSeen: true, confirmationExchangeRates: true, useSafeChainsListValidation: true, + bitcoinSupportEnabled: false, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) addSnapAccountEnabled: false, snapsAddSnapAccountModalDismissed: false, diff --git a/app/scripts/lib/snap-keyring/bitcoin-wallet-snap.ts b/app/scripts/lib/snap-keyring/bitcoin-wallet-snap.ts new file mode 100644 index 000000000000..b5340a095c09 --- /dev/null +++ b/app/scripts/lib/snap-keyring/bitcoin-wallet-snap.ts @@ -0,0 +1,25 @@ +import { SnapId } from '@metamask/snaps-sdk'; +import { Sender } from '@metamask/keyring-api'; +import { HandlerType } from '@metamask/snaps-utils'; +import { Json, JsonRpcRequest } from '@metamask/utils'; +// This dependency is still installed as part of the `package.json`, however +// the Snap is being pre-installed only for Flask build (for the moment). +import BitcoinWalletSnap from '@metamask/bitcoin-wallet-snap/dist/preinstalled-snap.json'; +import { handleSnapRequest } from '../../../../ui/store/actions'; + +export const BITCOIN_WALLET_SNAP_ID: SnapId = + BitcoinWalletSnap.snapId as SnapId; + +export class BitcoinWalletSnapSender implements Sender { + send = async (request: JsonRpcRequest): Promise => { + // We assume the caller of this module is aware of this. If we try to use this module + // without having the pre-installed Snap, this will likely throw an error in + // the `handleSnapRequest` action. + return (await handleSnapRequest({ + origin: 'metamask', + snapId: BITCOIN_WALLET_SNAP_ID, + handler: HandlerType.OnKeyringRequest, + request, + })) as Json; + }; +} diff --git a/app/scripts/lib/snap-keyring/keyring-snaps-permissions.ts b/app/scripts/lib/snap-keyring/keyring-snaps-permissions.ts index bdcd29acc9cb..ae6264cc1880 100644 --- a/app/scripts/lib/snap-keyring/keyring-snaps-permissions.ts +++ b/app/scripts/lib/snap-keyring/keyring-snaps-permissions.ts @@ -11,6 +11,7 @@ const PORTFOLIO_ORIGINS: string[] = [ 'https://portfolio.metamask.io', ///: BEGIN:ONLY_INCLUDE_IF(build-flask) 'https://dev.portfolio.metamask.io', + 'https://stage.portfolio.metamask.io', 'https://ramps-dev.portfolio.metamask.io', ///: END:ONLY_INCLUDE_IF ]; diff --git a/app/scripts/lib/transaction/decode/four-byte.test.ts b/app/scripts/lib/transaction/decode/four-byte.test.ts new file mode 100644 index 000000000000..da34598d6cdb --- /dev/null +++ b/app/scripts/lib/transaction/decode/four-byte.test.ts @@ -0,0 +1,300 @@ +import { + FOUR_BYTE_RESPONSE, + FOUR_BYTE_RESPONSE_NESTED, + TRANSACTION_DATA_FOUR_BYTE, + TRANSACTION_DATA_SOURCIFY_NESTED, +} from '../../../../../test/data/confirmations/transaction-decode'; +import { decodeTransactionDataWithFourByte } from './four-byte'; + +describe('Four Byte', () => { + const fetchMock = jest.fn(); + + beforeEach(() => { + jest.spyOn(global, 'fetch').mockImplementation(fetchMock); + }); + + describe('decodeTransactionDataWithFourByte', () => { + it('returns expected data', async () => { + fetchMock.mockResolvedValue({ + ok: true, + json: async () => FOUR_BYTE_RESPONSE, + }); + + const result = await decodeTransactionDataWithFourByte( + TRANSACTION_DATA_FOUR_BYTE, + ); + + expect(result).toMatchInlineSnapshot(` + { + "name": "someOtherFunction", + "params": [ + { + "children": undefined, + "name": undefined, + "type": "address", + "value": "0xec8507EcF7e946992294F06423A79835a3226846", + }, + { + "children": undefined, + "name": undefined, + "type": "uint256", + "value": { + "hex": "0x64", + "type": "BigNumber", + }, + }, + ], + } + `); + }); + + it('returns expected data with tuples and arrays', async () => { + fetchMock.mockResolvedValue({ + ok: true, + json: async () => FOUR_BYTE_RESPONSE_NESTED, + }); + + const result = await decodeTransactionDataWithFourByte( + TRANSACTION_DATA_SOURCIFY_NESTED, + ); + + expect(result).toMatchInlineSnapshot(` + { + "name": "permit", + "params": [ + { + "children": undefined, + "name": undefined, + "type": "address", + "value": "0xBe3be93fFAD7d417C08124B43286f4476C006AFe", + }, + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": undefined, + "name": undefined, + "type": "address", + "value": "0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B", + }, + { + "children": undefined, + "name": undefined, + "type": "uint160", + "value": { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + }, + { + "children": undefined, + "name": undefined, + "type": "uint48", + "value": 1749259022275, + }, + { + "children": undefined, + "name": undefined, + "type": "uint48", + "value": 0, + }, + ], + "name": "Item 1", + "type": "tuple", + "value": [ + "0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + }, + { + "children": [ + { + "children": undefined, + "name": undefined, + "type": "address", + "value": "0x4385328cc4D643Ca98DfEA734360C0F596C83449", + }, + { + "children": undefined, + "name": undefined, + "type": "uint160", + "value": { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + }, + { + "children": undefined, + "name": undefined, + "type": "uint48", + "value": 1749259022275, + }, + { + "children": undefined, + "name": undefined, + "type": "uint48", + "value": 0, + }, + ], + "name": "Item 2", + "type": "tuple", + "value": [ + "0x4385328cc4D643Ca98DfEA734360C0F596C83449", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + }, + { + "children": [ + { + "children": undefined, + "name": undefined, + "type": "address", + "value": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + }, + { + "children": undefined, + "name": undefined, + "type": "uint160", + "value": { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + }, + { + "children": undefined, + "name": undefined, + "type": "uint48", + "value": 1749259022275, + }, + { + "children": undefined, + "name": undefined, + "type": "uint48", + "value": 0, + }, + ], + "name": "Item 3", + "type": "tuple", + "value": [ + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + }, + ], + "name": undefined, + "type": "tuple[]", + "value": [ + [ + "0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + [ + "0x4385328cc4D643Ca98DfEA734360C0F596C83449", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + [ + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + ], + }, + { + "children": undefined, + "name": undefined, + "type": "address", + "value": "0xCC97F2E548ab94F40e5ADf473F596CEd83B6ee0a", + }, + { + "children": undefined, + "name": undefined, + "type": "uint256", + "value": { + "hex": "0x019747f66fc3", + "type": "BigNumber", + }, + }, + ], + "name": undefined, + "type": "tuple", + "value": [ + [ + [ + "0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + [ + "0x4385328cc4D643Ca98DfEA734360C0F596C83449", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + [ + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + ], + "0xCC97F2E548ab94F40e5ADf473F596CEd83B6ee0a", + { + "hex": "0x019747f66fc3", + "type": "BigNumber", + }, + ], + }, + { + "children": undefined, + "name": undefined, + "type": "bytes", + "value": "0x56e1fabfaf96d309b0039896d1f68b51e27f0e25b4481db8cad059b7e8db95d918bdc43ab1d03a0b6a84c7ea2219bf01133ceab4e7ca4e38055e4ed8af78a63b1b", + }, + ], + } + `); + }); + }); +}); diff --git a/app/scripts/lib/transaction/decode/four-byte.ts b/app/scripts/lib/transaction/decode/four-byte.ts new file mode 100644 index 000000000000..5861172fb60d --- /dev/null +++ b/app/scripts/lib/transaction/decode/four-byte.ts @@ -0,0 +1,132 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { addHexPrefix } from 'ethereumjs-util'; +import { Interface, ParamType } from '@ethersproject/abi'; +import { Hex, createProjectLogger } from '@metamask/utils'; +import { + DecodedTransactionDataMethod, + DecodedTransactionDataParam, +} from '../../../../../shared/types/transaction-decode'; +import { getMethodFrom4Byte } from '../../../../../shared/lib/four-byte'; + +const log = createProjectLogger('four-byte'); + +export async function decodeTransactionDataWithFourByte( + transactionData: string, +): Promise { + const fourBytePrefix = transactionData.slice(0, 10); + + const signature = (await getMethodFrom4Byte(fourBytePrefix)) as Hex; + + if (!signature) { + return undefined; + } + + const name = signature.split('(')[0]; + const inputs = parseSignature(signature); + + log('Generated inputs', inputs); + + const valueData = addHexPrefix(transactionData.slice(10)); + const values = Interface.getAbiCoder().decode(inputs, valueData) as any[]; + + const params = inputs.map((input, index) => + decodeParam(input, index, values), + ); + + return { name, params }; +} + +function decodeParam( + input: ParamType, + index: number, + values: any[], +): DecodedTransactionDataParam { + const value = values[index] as any[]; + const { type, name } = input; + + let children = input.components?.map((child, childIndex) => + decodeParam(child, childIndex, value), + ); + + if (type.endsWith('[]')) { + const childType = type.slice(0, -2); + + children = value.map((_arrayItem, arrayIndex) => { + const childName = `Item ${arrayIndex + 1}`; + + return decodeParam( + { ...input, name: childName, type: childType } as ParamType, + arrayIndex, + value, + ); + }); + } + + return { + name, + type, + value, + children, + }; +} + +function parseSignature(signature: string): ParamType[] { + let typeString = signature.slice(signature.indexOf('(') + 1, -1); + const nested = []; + + while (typeString.includes('(')) { + const nestedBrackets = findFirstNestedBrackets(typeString); + + if (!nestedBrackets) { + break; + } + + nested.push(nestedBrackets.value); + + typeString = `${typeString.slice(0, nestedBrackets.start)}${ + nested.length - 1 + }#${typeString.slice(nestedBrackets.end + 1)}`; + } + + return createInput(typeString, nested); +} + +function createInput(typeString: string, nested: string[]): ParamType[] { + return typeString.split(',').map((value) => { + const parts = value.split('#'); + + const nestedIndex = parts.length > 1 ? parseInt(parts[0], 10) : undefined; + const type = nestedIndex === undefined ? value : `tuple${parts[1] ?? ''}`; + + const components = + nestedIndex === undefined + ? undefined + : createInput(nested[nestedIndex], nested); + + return { + type, + components, + } as ParamType; + }); +} + +function findFirstNestedBrackets( + value: string, +): { start: number; end: number; value: string } | undefined { + let start = -1; + + for (let i = 0; i < value.length; i++) { + if (value[i] === '(') { + start = i; + } else if (value[i] === ')' && start !== -1) { + return { + start, + end: i, + value: value.slice(start + 1, i), + }; + } + } + + return undefined; +} diff --git a/app/scripts/lib/transaction/decode/proxy.test.ts b/app/scripts/lib/transaction/decode/proxy.test.ts new file mode 100644 index 000000000000..d137fe567c73 --- /dev/null +++ b/app/scripts/lib/transaction/decode/proxy.test.ts @@ -0,0 +1,45 @@ +import EthQuery from '@metamask/eth-query'; +import { getContractProxyAddress } from './proxy'; + +const CONTRACT_ADDRESS_MOCK = '0x456'; + +function createEthQueryMock(storageValues: string[]): EthQuery { + const ethQuery = { + eth_getStorageAt: jest.fn(), + }; + + for (const storageValue of storageValues) { + ethQuery.eth_getStorageAt.mockImplementationOnce( + (_contractAddress, _storageSlot, _blockNumber, cb) => + cb(null, storageValue), + ); + } + + return ethQuery as unknown as EthQuery; +} + +describe('Proxy', () => { + describe('getContractProxyAddress', () => { + it('returns undefined if all responses empty', async () => { + const ethQuery = createEthQueryMock([ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ]); + + expect( + await getContractProxyAddress(CONTRACT_ADDRESS_MOCK, ethQuery), + ).toBeUndefined(); + }); + + it('returns first non-empty response', async () => { + const ethQuery = createEthQueryMock([ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '00000000000000000000000000123', + ]); + + expect( + await getContractProxyAddress(CONTRACT_ADDRESS_MOCK, ethQuery), + ).toBe('0x123'); + }); + }); +}); diff --git a/app/scripts/lib/transaction/decode/proxy.ts b/app/scripts/lib/transaction/decode/proxy.ts new file mode 100644 index 000000000000..19d5887acd13 --- /dev/null +++ b/app/scripts/lib/transaction/decode/proxy.ts @@ -0,0 +1,36 @@ +import { query } from '@metamask/controller-utils'; +import EthQuery from '@metamask/eth-query'; +import { Hex } from '@metamask/utils'; +import { addHexPrefix, stripHexPrefix } from 'ethereumjs-util'; + +const IMPLEMENTATION_STORAGE_SLOTS = [ + // org.zeppelinos.proxy.implementation + '0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3', + + // eip1967.proxy.implementation + '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc', +]; + +const EMPTY_RESULT = '0'.padEnd(64, '0'); + +export async function getContractProxyAddress( + contractAddress: Hex, + ethQuery: EthQuery, +): Promise { + const responses = await Promise.all( + IMPLEMENTATION_STORAGE_SLOTS.map( + (storageSlot) => + query(ethQuery, 'eth_getStorageAt', [ + contractAddress, + storageSlot, + 'latest', + ]) as Promise, + ), + ); + + const result = responses.find( + (response) => stripHexPrefix(response) !== EMPTY_RESULT, + ); + + return result && (addHexPrefix(result.slice(26)) as Hex | undefined); +} diff --git a/app/scripts/lib/transaction/decode/sourcify.test.ts b/app/scripts/lib/transaction/decode/sourcify.test.ts new file mode 100644 index 000000000000..301e42012047 --- /dev/null +++ b/app/scripts/lib/transaction/decode/sourcify.test.ts @@ -0,0 +1,332 @@ +import { + SOURCIFY_RESPONSE, + SOURCIFY_RESPONSE_NESTED, + TRANSACTION_DATA_SOURCIFY, + TRANSACTION_DATA_SOURCIFY_NESTED, +} from '../../../../../test/data/confirmations/transaction-decode'; +import { decodeTransactionDataWithSourcify } from './sourcify'; + +const CONTRACT_ADDRESS_MOCK = '0x456'; +const CHAIN_ID_MOCK = '0x123'; + +describe('Sourcify', () => { + const fetchMock = jest.fn(); + + beforeEach(() => { + jest.spyOn(global, 'fetch').mockImplementation(fetchMock); + }); + + describe('decodeTransactionDataWithSourcify', () => { + it('returns expected data', async () => { + fetchMock.mockResolvedValue({ + ok: true, + json: async () => SOURCIFY_RESPONSE, + }); + + const result = await decodeTransactionDataWithSourcify( + TRANSACTION_DATA_SOURCIFY, + CONTRACT_ADDRESS_MOCK, + CHAIN_ID_MOCK, + ); + + expect(result).toMatchInlineSnapshot(` + { + "description": "Transfer tokens", + "name": "transfer", + "params": [ + { + "children": undefined, + "description": "The address to transfer to", + "name": "to", + "type": "address", + "value": "0xec8507EcF7e946992294F06423A79835a3226846", + }, + { + "children": undefined, + "description": "The amount to transfer", + "name": "value", + "type": "uint256", + "value": { + "hex": "0x64", + "type": "BigNumber", + }, + }, + ], + } + `); + }); + + it('returns expected data with tuples and arrays', async () => { + fetchMock.mockResolvedValue({ + ok: true, + json: async () => SOURCIFY_RESPONSE_NESTED, + }); + + const result = await decodeTransactionDataWithSourcify( + TRANSACTION_DATA_SOURCIFY_NESTED, + CONTRACT_ADDRESS_MOCK, + CHAIN_ID_MOCK, + ); + + expect(result).toMatchInlineSnapshot(` + { + "description": "Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature", + "name": "permit", + "params": [ + { + "children": undefined, + "description": "The owner of the tokens being approved", + "name": "owner", + "type": "address", + "value": "0xBe3be93fFAD7d417C08124B43286f4476C006AFe", + }, + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": undefined, + "description": undefined, + "name": "token", + "type": "address", + "value": "0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B", + }, + { + "children": undefined, + "description": undefined, + "name": "amount", + "type": "uint160", + "value": { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + }, + { + "children": undefined, + "description": undefined, + "name": "expiration", + "type": "uint48", + "value": 1749259022275, + }, + { + "children": undefined, + "description": undefined, + "name": "nonce", + "type": "uint48", + "value": 0, + }, + ], + "description": undefined, + "name": "Item 1", + "type": "tuple", + "value": [ + "0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + }, + { + "children": [ + { + "children": undefined, + "description": undefined, + "name": "token", + "type": "address", + "value": "0x4385328cc4D643Ca98DfEA734360C0F596C83449", + }, + { + "children": undefined, + "description": undefined, + "name": "amount", + "type": "uint160", + "value": { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + }, + { + "children": undefined, + "description": undefined, + "name": "expiration", + "type": "uint48", + "value": 1749259022275, + }, + { + "children": undefined, + "description": undefined, + "name": "nonce", + "type": "uint48", + "value": 0, + }, + ], + "description": undefined, + "name": "Item 2", + "type": "tuple", + "value": [ + "0x4385328cc4D643Ca98DfEA734360C0F596C83449", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + }, + { + "children": [ + { + "children": undefined, + "description": undefined, + "name": "token", + "type": "address", + "value": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + }, + { + "children": undefined, + "description": undefined, + "name": "amount", + "type": "uint160", + "value": { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + }, + { + "children": undefined, + "description": undefined, + "name": "expiration", + "type": "uint48", + "value": 1749259022275, + }, + { + "children": undefined, + "description": undefined, + "name": "nonce", + "type": "uint48", + "value": 0, + }, + ], + "description": undefined, + "name": "Item 3", + "type": "tuple", + "value": [ + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + }, + ], + "description": undefined, + "name": "details", + "type": "tuple[]", + "value": [ + [ + "0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + [ + "0x4385328cc4D643Ca98DfEA734360C0F596C83449", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + [ + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + ], + }, + { + "children": undefined, + "description": undefined, + "name": "spender", + "type": "address", + "value": "0xCC97F2E548ab94F40e5ADf473F596CEd83B6ee0a", + }, + { + "children": undefined, + "description": undefined, + "name": "sigDeadline", + "type": "uint256", + "value": { + "hex": "0x019747f66fc3", + "type": "BigNumber", + }, + }, + ], + "description": "Data signed over by the owner specifying the terms of approval", + "name": "permitBatch", + "type": "tuple", + "value": [ + [ + [ + "0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + [ + "0x4385328cc4D643Ca98DfEA734360C0F596C83449", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + [ + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + { + "hex": "0xffffffffffffffffffffffffffffffffffffffff", + "type": "BigNumber", + }, + 1749259022275, + 0, + ], + ], + "0xCC97F2E548ab94F40e5ADf473F596CEd83B6ee0a", + { + "hex": "0x019747f66fc3", + "type": "BigNumber", + }, + ], + }, + { + "children": undefined, + "description": "The owner's signature over the permit data", + "name": "signature", + "type": "bytes", + "value": "0x56e1fabfaf96d309b0039896d1f68b51e27f0e25b4481db8cad059b7e8db95d918bdc43ab1d03a0b6a84c7ea2219bf01133ceab4e7ca4e38055e4ed8af78a63b1b", + }, + ], + } + `); + }); + }); +}); diff --git a/app/scripts/lib/transaction/decode/sourcify.ts b/app/scripts/lib/transaction/decode/sourcify.ts new file mode 100644 index 000000000000..b7939d379c75 --- /dev/null +++ b/app/scripts/lib/transaction/decode/sourcify.ts @@ -0,0 +1,181 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { FunctionFragment, Interface, ParamType } from '@ethersproject/abi'; +import { Hex, createProjectLogger } from '@metamask/utils'; +import { + DecodedTransactionDataMethod, + DecodedTransactionDataParam, +} from '../../../../../shared/types/transaction-decode'; + +const log = createProjectLogger('sourcify'); + +export type SourcifyResponse = { + files: { + name: string; + content: string; + }[]; +}; + +export type SourcifyMetadata = { + output: { + abi: { + inputs: { name: string; type: string }[]; + }[]; + devdoc?: { + methods: { + [signature: string]: { + details?: string; + params?: { [name: string]: string }; + }; + }; + }; + userdoc?: { + methods: { + [signature: string]: { + notice?: string; + params?: { [name: string]: string }; + }; + }; + }; + }; +}; + +export async function decodeTransactionDataWithSourcify( + transactionData: Hex, + contractAddress: Hex, + chainId: Hex, +): Promise { + const metadata = await fetchSourcifyMetadata(contractAddress, chainId); + + log('Retrieved Sourcify metadata', { + contractAddress, + chainId, + metadata, + }); + + const { abi } = metadata.output; + const contractInterface = new Interface(abi); + const functionSignature = transactionData.slice(0, 10); + + let functionData: FunctionFragment | undefined; + + try { + functionData = contractInterface.getFunction(functionSignature); + } catch (e) { + // Ignore + } + + if (!functionData) { + log('Failed to find function in ABI', functionSignature, abi); + return undefined; + } + + const { name, inputs } = functionData; + const signature = buildSignature(name, inputs); + const userDoc = metadata.output.userdoc?.methods[signature]; + const devDoc = metadata.output.devdoc?.methods[signature]; + const description = userDoc?.notice ?? devDoc?.details; + + log('Extracted NatSpec', { signature, userDoc, devDoc }); + + const values = contractInterface.decodeFunctionData( + functionSignature, + transactionData, + ) as any[]; + + const params = inputs.map((input, index) => + decodeParam(input, index, values, userDoc, devDoc), + ); + + return { + name, + description, + params, + }; +} + +function decodeParam( + input: ParamType, + index: number, + values: any[], + userDoc: any, + devDoc: any, +): DecodedTransactionDataParam { + const { name: paramName, type, components } = input; + + const paramDescription = + userDoc?.params?.[paramName] ?? devDoc?.params?.[paramName]; + + const value = values[index]; + + let children = components?.map((child, childIndex) => + decodeParam(child, childIndex, value, {}, {}), + ); + + if (type.endsWith('[]')) { + const childType = type.slice(0, -2); + + children = (value as any[]).map((_arrayItem, arrayIndex) => { + const childName = `Item ${arrayIndex + 1}`; + + return decodeParam( + { ...input, name: childName, type: childType } as ParamType, + arrayIndex, + value, + {}, + {}, + ); + }); + } + + return { + name: paramName, + description: paramDescription, + type, + value, + children, + }; +} + +async function fetchSourcifyMetadata(address: Hex, chainId: Hex) { + const response = await fetchSourcifyFiles(address, chainId); + + const metadata = response.files?.find((file) => + file.name.includes('metadata.json'), + ); + + if (!metadata) { + throw new Error('Metadata not found'); + } + + return JSON.parse(metadata.content) as SourcifyMetadata; +} + +async function fetchSourcifyFiles( + address: Hex, + chainId: Hex, +): Promise { + const chainIdDecimal = parseInt(chainId, 16); + + const respose = await fetch( + `https://sourcify.dev/server/files/any/${chainIdDecimal}/${address}`, + ); + + if (!respose.ok) { + throw new Error('Failed to fetch Sourcify files'); + } + + return respose.json(); +} + +function buildSignature(name: string | undefined, inputs: ParamType[]): string { + const types = inputs.map((input) => + input.components?.length + ? `${buildSignature(undefined, input.components)}${ + input.type.endsWith('[]') ? '[]' : '' + }` + : input.type, + ); + + return `${name ?? ''}(${types.join(',')})`; +} diff --git a/app/scripts/lib/transaction/decode/uniswap-commands.ts b/app/scripts/lib/transaction/decode/uniswap-commands.ts new file mode 100644 index 000000000000..b73f22c780cd --- /dev/null +++ b/app/scripts/lib/transaction/decode/uniswap-commands.ts @@ -0,0 +1,610 @@ +export const UNISWAP_ROUTER_COMMANDS = { + '0': { + name: 'V3_SWAP_EXACT_IN', + params: [ + { + type: 'address', + description: 'The recipient of the output of the trade', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The amount of input tokens for the trade', + name: 'amountIn', + }, + { + type: 'uint256', + description: 'The minimum amount of output tokens the user wants', + name: 'amountOutMin', + }, + { + type: 'bytes', + description: 'The UniswapV3 encoded path to trade along', + name: 'path', + }, + { + type: 'bool', + description: + 'A flag for whether the input tokens should come from the msg.sender (through Permit2) or whether the funds are already in the UniversalRouter', + name: 'payerIsUser', + }, + ], + }, + '1': { + name: 'V3_SWAP_EXACT_OUT', + params: [ + { + type: 'address', + description: 'The recipient of the output of the trade', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The amount of output tokens to receive', + name: 'amountOut', + }, + { + type: 'uint256', + description: 'The maximum number of input tokens that should be spent', + name: 'amountInMax', + }, + { + type: 'bytes', + description: 'The UniswapV3 encoded path to trade along', + name: 'path', + }, + { + type: 'bool', + description: + 'A flag for whether the input tokens should come from the msg.sender (through Permit2) or whether the funds are already in the UniversalRouter', + name: 'payerIsUser', + }, + ], + }, + '2': { + name: 'PERMIT2_TRANSFER_FROM', + params: [ + { + type: 'address', + description: 'The token to fetch from Permit2', + name: 'token', + }, + { + type: 'address', + description: 'The recipient of the tokens fetched', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The amount of token to fetch', + name: 'amount', + }, + ], + }, + '3': { + name: 'PERMIT2_PERMIT_BATCH', + params: [ + { + type: 'bytes', + description: + 'A PermitBatch struct outlining all of the Permit2 permits to execute.', + name: 'batch', + }, + { + type: 'bytes', + description: 'The signature to provide to Permit2', + name: 'data', + }, + ], + }, + '4': { + name: 'SWEEP', + params: [ + { + type: 'address', + description: 'The ERC20 token to sweep (or Constants.ETH for ETH)', + name: 'token', + }, + { + type: 'address', + description: 'The recipient of the sweep', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The minimum required tokens to receive from the sweep', + name: 'amountMin', + }, + ], + }, + '5': { + name: 'TRANSFER', + params: [ + { + type: 'address', + description: 'The ERC20 token to transfer (or Constants.ETH for ETH)', + name: 'token', + }, + { + type: 'address', + description: 'The recipient of the transfer', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The amount to transfer', + name: 'value', + }, + ], + }, + '6': { + name: 'PAY_PORTION', + params: [ + { + type: 'address', + description: 'The ERC20 token to transfer (or Constants.ETH for ETH)', + name: 'token', + }, + { + type: 'address', + description: 'The recipient of the transfer', + name: 'recipient', + }, + { + type: 'uint256', + description: + 'In basis points, the percentage of the contract’s balance to transfer', + name: 'bips', + }, + ], + }, + '8': { + name: 'V2_SWAP_EXACT_IN', + params: [ + { + type: 'address', + description: 'The recipient of the output of the trade', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The amount of input tokens for the trade', + name: 'amountIn', + }, + { + type: 'uint256', + description: 'The minimum amount of output tokens the user wants', + name: 'amountOutMin', + }, + { + type: 'address[]', + description: 'The UniswapV2 token path to trade along', + name: 'path', + }, + { + type: 'bool', + description: + 'A flag for whether the input tokens should come from the msg.sender (through Permit2) or whether the funds are already in the UniversalRouter', + name: 'payerIsUser', + }, + ], + }, + '9': { + name: 'V2_SWAP_EXACT_OUT', + params: [ + { + type: 'address', + description: 'The recipient of the output of the trade', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The amount of output tokens to receive', + name: 'amountOut', + }, + { + type: 'uint256', + description: 'The maximum number of input tokens that should be spent', + name: 'amountInMax', + }, + { + type: 'address[]', + description: 'The UniswapV2 token path to trade along', + name: 'path', + }, + { + type: 'bool', + description: + 'A flag for whether the input tokens should come from the msg.sender (through Permit2) or whether the funds are already in the UniversalRouter', + name: 'payerIsUser', + }, + ], + }, + '10': { + name: 'PERMIT2_PERMIT', + params: [ + { + type: 'bytes', + description: + 'A PermitSingle struct outlining the Permit2 permit to execute', + name: 'permitSingle', + }, + { + type: 'bytes', + description: 'The signature to provide to Permit2', + name: 'signature', + }, + ], + }, + '11': { + name: 'WRAP_ETH', + params: [ + { + type: 'address', + description: 'The recipient of the WETH', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The amount of ETH to wrap', + name: 'amountMin', + }, + ], + }, + '12': { + name: 'UNWRAP_WETH', + params: [ + { + type: 'address', + description: 'The recipient of the ETH', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The minimum required ETH to receive from the unwrapping', + name: 'amountMin', + }, + ], + }, + '13': { + name: 'PERMIT2_TRANSFER_FROM_BATCH', + params: [ + { + type: 'bytes', + description: + 'An array of AllowanceTransferDetails structs that each describe a Permit2 transfer to perform', + name: 'batchDetails', + }, + ], + }, + '16': { + name: 'SEAPORT', + params: [ + { + type: 'uint256', + description: 'The ETH value to forward to the Seaport contract', + name: 'value', + }, + { + type: 'bytes', + description: 'The calldata to use to call the Seaport contract', + name: 'data', + }, + ], + }, + '17': { + name: 'LOOKS_RARE_721', + params: [ + { + type: 'uint256', + description: 'The ETH value to forward to the LooksRare contract', + name: 'value', + }, + { + type: 'bytes', + description: 'The calldata to use to call the LooksRare contract', + name: 'data', + }, + { + type: 'address', + description: 'The recipient of the ERC721', + name: 'recipient', + }, + { + type: 'address', + description: 'The ERC721 token address', + name: 'token', + }, + { + type: 'uint256', + description: 'The ID of the ERC721', + name: 'id', + }, + ], + }, + '18': { + name: 'NFTX', + params: [ + { + type: 'uint256', + description: 'The ETH value to forward to the NFTX contract', + name: 'value', + }, + { + type: 'bytes', + description: 'The calldata to use to call the NFTX contract', + name: 'data', + }, + ], + }, + '19': { + name: 'CRYPTOPUNKS', + params: [ + { + type: 'uint256', + description: 'The PunkID to purchase', + name: 'punkId', + }, + { + type: 'address', + description: 'The recipient for the cryptopunk', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The ETH value to forward to the Cryptopunks contract', + name: 'value', + }, + ], + }, + '20': { + name: 'LOOKS_RARE_1155', + params: [ + { + type: 'uint256', + description: 'The ETH value to forward to the LooksRare contract', + name: 'value', + }, + { + type: 'bytes', + description: 'The calldata to use to call the LooksRare contract', + name: 'data', + }, + { + type: 'address', + description: 'The recipient of the ERC1155', + name: 'recipient', + }, + { + type: 'address', + description: 'The ERC1155 token address', + name: 'token', + }, + { + type: 'uint256', + description: 'The ID of the ERC1155', + name: 'id', + }, + { + type: 'uint256', + description: 'The amount of the ERC1155 to transfer', + name: 'amount', + }, + ], + }, + '21': { + name: 'OWNER_CHECK_721', + params: [ + { + type: 'address', + description: 'The required owner of the ERC721', + name: 'owner', + }, + { + type: 'address', + description: 'The ERC721 token address', + name: 'token', + }, + { + type: 'uint256', + description: 'The ID of the ERC721', + name: 'id', + }, + ], + }, + '22': { + name: 'OWNER_CHECK_1155', + params: [ + { + type: 'address', + description: 'The required owner of the ERC1155', + name: 'owner', + }, + { + type: 'address', + description: 'The ERC721 token address', + name: 'token', + }, + { + type: 'uint256', + description: 'The ID of the ERC1155', + name: 'id', + }, + { + type: 'uint256', + description: 'The minimum required amount of the ERC1155', + name: 'minBalance', + }, + ], + }, + '23': { + name: 'SWEEP_ERC721', + params: [ + { + type: 'address', + description: 'The ERC721 token address to transfer', + name: 'token', + }, + { + type: 'address', + description: 'The recipient of the transfer', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The token ID to transfer', + name: 'id', + }, + ], + }, + '24': { + name: 'X2Y2_721', + params: [ + { + type: 'uint256', + description: 'The ETH value to forward to the X2Y2 contract', + name: 'value', + }, + { + type: 'bytes', + description: 'The calldata to use to call the X2Y2 contract', + name: 'data', + }, + { + type: 'address', + description: 'The recipient of the ERC721', + name: 'recipient', + }, + { + type: 'address', + description: 'The ERC721 token address', + name: 'token', + }, + { + type: 'uint256', + description: 'The ID of the ERC721', + name: 'id', + }, + ], + }, + '25': { + name: 'SUDOSWAP', + params: [ + { + type: 'uint256', + description: 'The ETH value to forward to the Sudoswap contract', + name: 'value', + }, + { + type: 'bytes', + description: 'The calldata to use to call the Sudoswap contract', + name: 'data', + }, + ], + }, + '26': { + name: 'NFT20', + params: [ + { + type: 'uint256', + description: 'The ETH value to forward to the NFT20 contract', + name: 'value', + }, + { + type: 'bytes', + description: 'The calldata to use to call the NFT20 contract', + name: 'data', + }, + ], + }, + '27': { + name: 'X2Y2_1155', + params: [ + { + type: 'uint256', + description: 'The ETH value to forward to the X2Y2 contract', + name: 'value', + }, + { + type: 'bytes', + description: 'The calldata to use to call the X2Y2 contract', + name: 'data', + }, + { + type: 'address', + description: 'The recipient of the ERC1155', + name: 'recipient', + }, + { + type: 'address', + description: 'The ERC1155 token address', + name: 'token', + }, + { + type: 'uint256', + description: 'The ID of the ERC1155', + name: 'id', + }, + { + type: 'uint256', + description: 'The amount of the ERC1155 to transfer', + name: 'amount', + }, + ], + }, + '28': { + name: 'FOUNDATION', + params: [ + { + type: 'uint256', + description: 'The ETH value to forward to the Foundation contract', + name: 'value', + }, + { + type: 'bytes', + description: 'The calldata to use to call the Foundation contract', + name: 'data', + }, + { + type: 'address', + description: 'The recipient of the ERC721', + name: 'recipient', + }, + { + type: 'address', + description: 'The ERC721 token address', + name: 'token', + }, + { + type: 'uint256', + description: 'The ID of the ERC721', + name: 'id', + }, + ], + }, + '29': { + name: 'SWEEP_ERC1155', + params: [ + { + type: 'address', + description: 'The ERC1155 token address to sweep', + name: 'token', + }, + { + type: 'address', + description: 'The recipient of the sweep', + name: 'recipient', + }, + { + type: 'uint256', + description: 'The token ID to sweep', + name: 'id', + }, + { + type: 'uint256', + description: 'The minimum required tokens to receive from the sweep', + name: 'amount', + }, + ], + }, +}; diff --git a/app/scripts/lib/transaction/decode/uniswap.test.ts b/app/scripts/lib/transaction/decode/uniswap.test.ts new file mode 100644 index 000000000000..86455b8c9dc0 --- /dev/null +++ b/app/scripts/lib/transaction/decode/uniswap.test.ts @@ -0,0 +1,164 @@ +import { CHAIN_IDS } from '../../../../../shared/constants/network'; +import { TRANSACTION_DATA_UNISWAP } from '../../../../../test/data/confirmations/transaction-decode'; +import { + UNISWAP_UNIVERSAL_ROUTER_ADDRESSES, + decodeUniswapRouterTransactionData, +} from './uniswap'; + +describe('Uniswap', () => { + describe('decodeUniswapRouterTransactionData', () => { + it('returns undefined if invalid data', () => { + expect( + decodeUniswapRouterTransactionData({ + transactionData: '0x123', + contractAddress: + UNISWAP_UNIVERSAL_ROUTER_ADDRESSES[CHAIN_IDS.MAINNET][0], + chainId: CHAIN_IDS.MAINNET, + }), + ).toBeUndefined(); + }); + + it('returns undefined if contract address does not match chain', () => { + expect( + decodeUniswapRouterTransactionData({ + transactionData: TRANSACTION_DATA_UNISWAP, + contractAddress: '0x123', + chainId: CHAIN_IDS.MAINNET, + }), + ).toBeUndefined(); + }); + + it('returns expected commands', () => { + expect( + decodeUniswapRouterTransactionData({ + transactionData: TRANSACTION_DATA_UNISWAP, + contractAddress: + UNISWAP_UNIVERSAL_ROUTER_ADDRESSES[CHAIN_IDS.MAINNET][0], + chainId: CHAIN_IDS.MAINNET, + }), + ).toMatchInlineSnapshot(` + [ + { + "name": "WRAP_ETH", + "params": [ + { + "description": "The recipient of the WETH", + "name": "recipient", + "type": "address", + "value": "0x0000000000000000000000000000000000000002", + }, + { + "description": "The amount of ETH to wrap", + "name": "amountMin", + "type": "uint256", + "value": { + "hex": "0x5af3107a4000", + "type": "BigNumber", + }, + }, + ], + }, + { + "name": "V3_SWAP_EXACT_IN", + "params": [ + { + "description": "The recipient of the output of the trade", + "name": "recipient", + "type": "address", + "value": "0x0000000000000000000000000000000000000002", + }, + { + "description": "The amount of input tokens for the trade", + "name": "amountIn", + "type": "uint256", + "value": { + "hex": "0x5af3107a4000", + "type": "BigNumber", + }, + }, + { + "description": "The minimum amount of output tokens the user wants", + "name": "amountOutMin", + "type": "uint256", + "value": { + "hex": "0x04c418", + "type": "BigNumber", + }, + }, + { + "description": "The UniswapV3 encoded path to trade along", + "name": "path", + "type": "bytes", + "value": [ + { + "firstAddress": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "secondAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "tickSpacing": 500, + }, + ], + }, + { + "description": "A flag for whether the input tokens should come from the msg.sender (through Permit2) or whether the funds are already in the UniversalRouter", + "name": "payerIsUser", + "type": "bool", + "value": false, + }, + ], + }, + { + "name": "PAY_PORTION", + "params": [ + { + "description": "The ERC20 token to transfer (or Constants.ETH for ETH)", + "name": "token", + "type": "address", + "value": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + }, + { + "description": "The recipient of the transfer", + "name": "recipient", + "type": "address", + "value": "0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47", + }, + { + "description": "In basis points, the percentage of the contract’s balance to transfer", + "name": "bips", + "type": "uint256", + "value": { + "hex": "0x19", + "type": "BigNumber", + }, + }, + ], + }, + { + "name": "SWEEP", + "params": [ + { + "description": "The ERC20 token to sweep (or Constants.ETH for ETH)", + "name": "token", + "type": "address", + "value": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + }, + { + "description": "The recipient of the sweep", + "name": "recipient", + "type": "address", + "value": "0x0000000000000000000000000000000000000001", + }, + { + "description": "The minimum required tokens to receive from the sweep", + "name": "amountMin", + "type": "uint256", + "value": { + "hex": "0x04c418", + "type": "BigNumber", + }, + }, + ], + }, + ] + `); + }); + }); +}); diff --git a/app/scripts/lib/transaction/decode/uniswap.ts b/app/scripts/lib/transaction/decode/uniswap.ts new file mode 100644 index 000000000000..c38e204180fd --- /dev/null +++ b/app/scripts/lib/transaction/decode/uniswap.ts @@ -0,0 +1,214 @@ +import { Interface, TransactionDescription } from '@ethersproject/abi'; +import { Hex } from '@metamask/utils'; +import { addHexPrefix, stripHexPrefix } from 'ethereumjs-util'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; +import { UNISWAP_ROUTER_COMMANDS } from './uniswap-commands'; + +export type UniswapRouterCommand = { + name: string; + params: { + name: string; + type: string; + description: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any; + }[]; +}; + +export type UniswapPathPool = { + firstAddress: Hex; + tickSpacing: number; + secondAddress: Hex; +}; + +const ADDRESS_LENGTH = 40; +const TICK_SPACING_LENGTH = 6; + +export const UNISWAP_UNIVERSAL_ROUTER_ADDRESSES = { + [CHAIN_IDS.ARBITRUM]: [ + '0x4C60051384bd2d3C01bfc845Cf5F4b44bcbE9de5', + '0xeC8B0F7Ffe3ae75d7FfAb09429e3675bb63503e4', + '0x5E325eDA8064b456f4781070C0738d849c824258', + ], + [CHAIN_IDS.AVALANCHE]: [ + '0x82635AF6146972cD6601161c4472ffe97237D292', + '0x4Dae2f939ACf50408e13d58534Ff8c2776d45265', + ], + [CHAIN_IDS.BASE]: [ + '0xeC8B0F7Ffe3ae75d7FfAb09429e3675bb63503e4', + '0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD', + ], + [CHAIN_IDS.BSC]: [ + '0x5Dc88340E1c5c6366864Ee415d6034cadd1A9897', + '0xeC8B0F7Ffe3ae75d7FfAb09429e3675bb63503e4', + '0x4Dae2f939ACf50408e13d58534Ff8c2776d45265', + ], + [CHAIN_IDS.MAINNET]: [ + '0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B', + '0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD', + ], + [CHAIN_IDS.OPTIMISM]: [ + '0xb555edF5dcF85f42cEeF1f3630a52A108E55A654', + '0xeC8B0F7Ffe3ae75d7FfAb09429e3675bb63503e4', + '0xCb1355ff08Ab38bBCE60111F1bb2B784bE25D7e8', + ], + [CHAIN_IDS.POLYGON]: [ + '0x4C60051384bd2d3C01bfc845Cf5F4b44bcbE9de5', + '0x643770E279d5D0733F21d6DC03A8efbABf3255B4', + '0xec7BE89e9d109e7e3Fec59c222CF297125FEFda2', + ], + [CHAIN_IDS.SEPOLIA]: ['0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD'], +} as Record; + +const ABI = [ + { + constant: true, + inputs: [ + { + name: 'commands', + type: 'bytes', + }, + { + name: 'inputs', + type: 'bytes[]', + }, + { + name: 'deadline', + type: 'uint256', + }, + ], + name: 'execute', + type: 'function', + }, + { + constant: true, + inputs: [ + { + name: 'commands', + type: 'bytes', + }, + { + name: 'inputs', + type: 'bytes[]', + }, + ], + name: 'execute', + type: 'function', + }, +]; + +export function decodeUniswapRouterTransactionData({ + transactionData, + contractAddress, + chainId, +}: { + transactionData: string; + contractAddress: string; + chainId: string; +}): UniswapRouterCommand[] | undefined { + const supportedAddresses = UNISWAP_UNIVERSAL_ROUTER_ADDRESSES[chainId]; + + if ( + !supportedAddresses + ?.map((address) => address.toLowerCase()) + .includes(contractAddress.toLowerCase()) + ) { + return undefined; + } + + const contractInterface = new Interface(ABI); + + let parsedTransactionData: TransactionDescription; + + try { + parsedTransactionData = contractInterface.parseTransaction({ + data: transactionData, + }); + } catch (error) { + return undefined; + } + + const commands = parsedTransactionData.args.commands as string; + const inputs = parsedTransactionData.args.inputs as string[]; + const commandBytes = commands.slice(2).match(/.{1,2}/gu) as string[]; + + return commandBytes + .map((commandByte, i) => decodeUniswapCommand(commandByte, inputs[i])) + .filter((command) => command !== undefined) as UniswapRouterCommand[]; +} + +function decodeUniswapCommand( + commandByte: string, + input: string, +): UniswapRouterCommand | undefined { + const commandValue = parseInt(commandByte, 16); + // eslint-disable-next-line no-bitwise + const commandIndex = commandValue & 0b11111; + + const data = + UNISWAP_ROUTER_COMMANDS[ + String(commandIndex) as keyof typeof UNISWAP_ROUTER_COMMANDS + ]; + + if (!data) { + return undefined; + } + + const types = data.params.map((param) => param.type); + const abiDecoder = Interface.getAbiCoder(); + const values = abiDecoder.decode(types, input); + const { name } = data; + + const params = data.params.map((param, index) => { + const { name: paramName, type, description } = param; + const rawData = values[index]; + const value = paramName === 'path' ? decodeUniswapPath(rawData) : rawData; + + return { name: paramName, type, value, description }; + }); + + return { + name, + params, + }; +} + +function decodeUniswapPath(rawPath: string): UniswapPathPool[] { + const pools: UniswapPathPool[] = []; + let remainingData = stripHexPrefix(rawPath); + let currentPool = {} as UniswapPathPool; + let isParsingAddress = true; + + while (remainingData.length) { + if (isParsingAddress) { + const address = addHexPrefix( + remainingData.slice(0, ADDRESS_LENGTH), + ) as Hex; + + if (currentPool.firstAddress) { + currentPool.secondAddress = address; + + pools.push(currentPool); + + currentPool = { + firstAddress: address, + } as UniswapPathPool; + } else { + currentPool.firstAddress = address; + } + + remainingData = remainingData.slice(ADDRESS_LENGTH); + } else { + currentPool.tickSpacing = parseInt( + remainingData.slice(0, TICK_SPACING_LENGTH), + 16, + ); + + remainingData = remainingData.slice(TICK_SPACING_LENGTH); + } + + isParsingAddress = !isParsingAddress; + } + + return pools; +} diff --git a/app/scripts/lib/transaction/decode/util.test.ts b/app/scripts/lib/transaction/decode/util.test.ts new file mode 100644 index 000000000000..66984b29cfe4 --- /dev/null +++ b/app/scripts/lib/transaction/decode/util.test.ts @@ -0,0 +1,106 @@ +import EthQuery from '@metamask/eth-query'; +import { + TRANSACTION_DATA_FOUR_BYTE, + TRANSACTION_DATA_SOURCIFY, + TRANSACTION_DATA_UNISWAP, + TRANSACTION_DECODE_FOUR_BYTE, + TRANSACTION_DECODE_SOURCIFY, + TRANSACTION_DECODE_UNISWAP, +} from '../../../../../test/data/confirmations/transaction-decode'; +import { decodeUniswapRouterTransactionData } from './uniswap'; +import { decodeTransactionData } from './util'; +import { decodeTransactionDataWithSourcify } from './sourcify'; +import { decodeTransactionDataWithFourByte } from './four-byte'; +import { getContractProxyAddress } from './proxy'; + +jest.mock('./uniswap'); +jest.mock('./sourcify'); +jest.mock('./four-byte'); +jest.mock('./proxy'); + +const CONTRACT_ADDRESS_MOCK = '0x456'; +const CHAIN_ID_MOCK = '0x123'; +const ETH_QUERY_MOCK = {} as EthQuery; + +describe('Transaction Decode Utils', () => { + const decodeUniswapRouterTransactionDataMock = jest.mocked( + decodeUniswapRouterTransactionData, + ); + + const decodeTransactionDataWithSourcifyMock = jest.mocked( + decodeTransactionDataWithSourcify, + ); + + const decodeTransactionDataWithFourByteMock = jest.mocked( + decodeTransactionDataWithFourByte, + ); + + const getContractProxyAddressMock = jest.mocked(getContractProxyAddress); + + beforeEach(() => { + jest.resetAllMocks(); + + decodeUniswapRouterTransactionDataMock.mockReturnValue(undefined); + decodeTransactionDataWithSourcifyMock.mockResolvedValue(undefined); + decodeTransactionDataWithFourByteMock.mockResolvedValue(undefined); + getContractProxyAddressMock.mockResolvedValue(undefined); + }); + + describe('decodeTransactionData', () => { + it('returns uniswap data', async () => { + decodeUniswapRouterTransactionDataMock.mockReturnValue( + TRANSACTION_DECODE_UNISWAP.data, + ); + + const result = await decodeTransactionData({ + transactionData: TRANSACTION_DATA_UNISWAP, + contractAddress: CONTRACT_ADDRESS_MOCK, + chainId: CHAIN_ID_MOCK, + ethQuery: ETH_QUERY_MOCK, + }); + + expect(result).toStrictEqual(TRANSACTION_DECODE_UNISWAP); + }); + + it('returns sourcify data', async () => { + decodeTransactionDataWithSourcifyMock.mockResolvedValue( + TRANSACTION_DECODE_SOURCIFY.data[0], + ); + + const result = await decodeTransactionData({ + transactionData: TRANSACTION_DATA_SOURCIFY, + contractAddress: CONTRACT_ADDRESS_MOCK, + chainId: CHAIN_ID_MOCK, + ethQuery: ETH_QUERY_MOCK, + }); + + expect(result).toStrictEqual(TRANSACTION_DECODE_SOURCIFY); + }); + + it('returns four byte data', async () => { + decodeTransactionDataWithFourByteMock.mockResolvedValue( + TRANSACTION_DECODE_FOUR_BYTE.data[0], + ); + + const result = await decodeTransactionData({ + transactionData: TRANSACTION_DATA_FOUR_BYTE, + contractAddress: CONTRACT_ADDRESS_MOCK, + chainId: CHAIN_ID_MOCK, + ethQuery: ETH_QUERY_MOCK, + }); + + expect(result).toStrictEqual(TRANSACTION_DECODE_FOUR_BYTE); + }); + + it('returns undefined if no data', async () => { + const result = await decodeTransactionData({ + transactionData: TRANSACTION_DATA_FOUR_BYTE, + contractAddress: CONTRACT_ADDRESS_MOCK, + chainId: CHAIN_ID_MOCK, + ethQuery: ETH_QUERY_MOCK, + }); + + expect(result).toBeUndefined(); + }); + }); +}); diff --git a/app/scripts/lib/transaction/decode/util.ts b/app/scripts/lib/transaction/decode/util.ts new file mode 100644 index 000000000000..d8bca103ff73 --- /dev/null +++ b/app/scripts/lib/transaction/decode/util.ts @@ -0,0 +1,128 @@ +import { Hex, createProjectLogger } from '@metamask/utils'; +import EthQuery from '@metamask/eth-query'; +import { + DecodedTransactionDataMethod, + DecodedTransactionDataParam, + DecodedTransactionDataResponse, + DecodedTransactionDataSource, +} from '../../../../../shared/types/transaction-decode'; +import { decodeUniswapRouterTransactionData } from './uniswap'; +import { decodeTransactionDataWithSourcify } from './sourcify'; +import { getContractProxyAddress } from './proxy'; +import { decodeTransactionDataWithFourByte } from './four-byte'; + +const log = createProjectLogger('transaction-decode'); + +export async function decodeTransactionData({ + transactionData, + contractAddress, + chainId, + ethQuery, +}: { + transactionData: Hex; + contractAddress: Hex; + chainId: Hex; + ethQuery: EthQuery; +}): Promise { + log('Decoding transaction data', { + transactionData, + contractAddress, + chainId, + }); + + const uniswapData = decodeUniswapRouterTransactionData({ + transactionData, + contractAddress, + chainId, + }); + + if (uniswapData) { + log('Decoded with Uniswap commands', uniswapData); + + return { + data: normalizeDecodedMethods(uniswapData), + source: DecodedTransactionDataSource.Uniswap, + }; + } + + const proxyAddress = await getContractProxyAddress(contractAddress, ethQuery); + + if (proxyAddress) { + log('Retrieved proxy implementation address', proxyAddress); + } + + const address = proxyAddress ?? contractAddress; + + const sourcifyData = decodeTransactionDataWithSourcify( + transactionData, + address, + chainId, + ); + + const fourByteData = decodeTransactionDataWithFourByte(transactionData); + + const [sourcifyResult, fourByteResult] = await Promise.allSettled([ + sourcifyData, + fourByteData, + ]); + + if (sourcifyResult.status === 'fulfilled' && sourcifyResult.value) { + log('Decoded data with Sourcify', sourcifyResult.value); + + return { + data: normalizeDecodedMethods([sourcifyResult.value]), + source: DecodedTransactionDataSource.Sourcify, + }; + } + + log('Failed to decode data with Sourcify', sourcifyResult); + + if (fourByteResult.status === 'fulfilled' && fourByteResult.value) { + log('Decoded data with 4Byte', fourByteResult.value); + + return { + data: normalizeDecodedMethods([fourByteResult.value]), + source: DecodedTransactionDataSource.FourByte, + }; + } + + log('Failed to decode data with 4Byte', fourByteResult); + + return undefined; +} + +function normalizeDecodedMethods( + methods: DecodedTransactionDataMethod[], +): DecodedTransactionDataMethod[] { + return methods.map((method) => normalizeDecodedMethod(method)); +} + +function normalizeDecodedMethod( + method: DecodedTransactionDataMethod, +): DecodedTransactionDataMethod { + return { + ...method, + params: method.params.map((param) => normalizeDecodedParam(param)), + }; +} + +function normalizeDecodedParam( + param: DecodedTransactionDataParam, +): DecodedTransactionDataParam { + return { + ...param, + value: normalizeDecodedParamValue(param.value), + children: param.children?.map((child) => normalizeDecodedParam(child)), + }; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function normalizeDecodedParamValue(value: any): any { + const hexValue = value._hex; + + if (hexValue) { + return parseInt(hexValue, 16); + } + + return value; +} diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 3439673a43cb..65837f5e53ed 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -322,6 +322,7 @@ import { updateSecurityAlertResponse } from './lib/ppom/ppom-util'; import createEvmMethodsToNonEvmAccountReqFilterMiddleware from './lib/createEvmMethodsToNonEvmAccountReqFilterMiddleware'; import { isEthAddress } from './lib/multichain/address'; import BridgeController from './controllers/bridge'; +import { decodeTransactionData } from './lib/transaction/decode/util'; export const METAMASK_CONTROLLER_EVENTS = { // Fired after state changes that impact the extension badge (unapproved msg count) @@ -3016,6 +3017,10 @@ export default class MetamaskController extends EventEmitter { preferencesController, ), ///: END:ONLY_INCLUDE_IF + setBitcoinSupportEnabled: + preferencesController.setBitcoinSupportEnabled.bind( + preferencesController, + ), setUseExternalNameSources: preferencesController.setUseExternalNameSources.bind( preferencesController, @@ -3738,10 +3743,18 @@ export default class MetamaskController extends EventEmitter { // E2E testing throwTestError: this.throwTestError.bind(this), + // NameController updateProposedNames: this.nameController.updateProposedNames.bind( this.nameController, ), setName: this.nameController.setName.bind(this.nameController), + + // Transaction Decode + decodeTransactionData: (request) => + decodeTransactionData({ + ...request, + ethQuery: new EthQuery(this.provider), + }), }; } diff --git a/builds.yml b/builds.yml index 0e5a69dd86d0..ec4370dd5b03 100644 --- a/builds.yml +++ b/builds.yml @@ -27,7 +27,6 @@ buildTypes: - REQUIRE_SNAPS_ALLOWLIST: true - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/iframe/6.5.0/index.html - ACCOUNT_SNAPS_DIRECTORY_URL: https://snaps.metamask.io/account-management - - BTC_BETA_SUPPORT: false # Main build uses the default browser manifest manifestOverrides: false # Build name used in multiple user-readable places @@ -72,7 +71,6 @@ buildTypes: - SEGMENT_WRITE_KEY_REF: SEGMENT_FLASK_WRITE_KEY - ACCOUNT_SNAPS_DIRECTORY_URL: https://metamask.github.io/snaps-directory-staging/main/account-management - EIP_4337_ENTRYPOINT: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789' - - BTC_BETA_SUPPORT: false isPrerelease: true manifestOverrides: ./app/build-types/flask/manifest/ buildNameOverride: MetaMask Flask diff --git a/jest.integration.config.js b/jest.integration.config.js new file mode 100644 index 000000000000..e6635bd5b695 --- /dev/null +++ b/jest.integration.config.js @@ -0,0 +1,37 @@ +module.exports = { + collectCoverageFrom: [ + '/shared/**/*.(js|ts|tsx)', + '/ui/**/*.(js|ts|tsx)', + ], + coverageDirectory: './coverage/integration', + coveragePathIgnorePatterns: ['.stories.*', '.snap', '.test.(js|ts|tsx)'], + coverageReporters: ['html', 'json'], + reporters: [ + 'default', + [ + 'jest-junit', + { + outputDirectory: 'test/test-results/integration', + outputName: 'junit.xml', + }, + ], + ], + restoreMocks: true, + setupFiles: [ + '/test/integration/config/setup.js', + '/test/integration/config/env.js', + ], + setupFilesAfterEnv: ['/test/integration/config/setupAfter.js'], + testMatch: ['/test/integration/**/*.test.(js|ts|tsx)'], + testPathIgnorePatterns: ['/test/integration/config/*'], + testTimeout: 5500, + // We have to specify the environment we are running in, which is jsdom. The + // default is 'node'. This can be modified *per file* using a comment at the + // head of the file. So it may be worthwhile to switch to 'node' in any + // background tests. + testEnvironment: 'jsdom', + testEnvironmentOptions: { + customExportConditions: ['node', 'node-addons'], + }, + workerIdleMemoryLimit: '500MB', +}; diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index daddd541a5f0..4506020c2bee 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -225,30 +225,25 @@ }, "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true } }, "@ethersproject/abi>@ethersproject/address": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/providers>@ethersproject/rlp": true } }, - "@ethersproject/abi>@ethersproject/bytes": { - "packages": { - "@ethersproject/abi>@ethersproject/logger": true - } - }, "@ethersproject/abi>@ethersproject/constants": { "packages": { "@ethersproject/bignumber": true @@ -256,7 +251,7 @@ }, "@ethersproject/abi>@ethersproject/keccak256": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@metamask/ethjs>js-sha3": true } }, @@ -272,18 +267,23 @@ }, "@ethersproject/abi>@ethersproject/strings": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@ethersproject/bignumber": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true, "bn.js": true } }, + "@ethersproject/bytes": { + "packages": { + "@ethersproject/abi>@ethersproject/logger": true + } + }, "@ethersproject/contracts": { "globals": { "setTimeout": true @@ -291,10 +291,10 @@ "packages": { "@ethersproject/abi": true, "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hdnode>@ethersproject/transactions": true, "@ethersproject/wallet>@ethersproject/abstract-provider": true @@ -303,12 +303,12 @@ "@ethersproject/hash": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/base64": true } }, @@ -324,16 +324,16 @@ "btoa": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true + "@ethersproject/bytes": true } }, "@ethersproject/hdnode": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/basex": true, "@ethersproject/hdnode>@ethersproject/pbkdf2": true, "@ethersproject/hdnode>@ethersproject/sha2": true, @@ -344,50 +344,50 @@ }, "@ethersproject/hdnode>@ethersproject/basex": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/properties": true + "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true } }, "@ethersproject/hdnode>@ethersproject/pbkdf2": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/sha2": true } }, "@ethersproject/hdnode>@ethersproject/sha2": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true, "ethereumjs-util>ethereum-cryptography>hash.js": true } }, "@ethersproject/hdnode>@ethersproject/signing-key": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true, "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/signing-key": true, "@ethersproject/providers>@ethersproject/rlp": true } }, "@ethersproject/hdnode>@ethersproject/wordlists": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true } }, @@ -403,12 +403,12 @@ }, "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hash>@ethersproject/base64": true, @@ -429,8 +429,8 @@ }, "@ethersproject/providers>@ethersproject/rlp": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@ethersproject/providers>@ethersproject/web": { @@ -440,20 +440,20 @@ "setTimeout": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/base64": true } }, "@ethersproject/wallet": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hdnode": true, @@ -466,20 +466,20 @@ }, "@ethersproject/wallet>@ethersproject/abstract-provider": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "@ethersproject/bignumber": true + "@ethersproject/bignumber": true, + "@ethersproject/bytes": true } }, "@ethersproject/wallet>@ethersproject/json-wallets": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode": true, "@ethersproject/hdnode>@ethersproject/pbkdf2": true, "@ethersproject/hdnode>@ethersproject/transactions": true, @@ -495,8 +495,8 @@ }, "@ethersproject/wallet>@ethersproject/random": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@keystonehq/bc-ur-registry-eth": { @@ -1525,9 +1525,9 @@ "setInterval": true }, "packages": { + "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller>@metamask/controller-utils": true, - "@metamask/gas-fee-controller>@metamask/polling-controller": true, "bn.js": true, "uuid": true } @@ -1550,18 +1550,6 @@ "eth-ens-namehash": true } }, - "@metamask/gas-fee-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true - } - }, "@metamask/jazzicon": { "globals": { "document.createElement": true, @@ -2206,7 +2194,7 @@ "setInterval": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/eth-query": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index daddd541a5f0..4506020c2bee 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -225,30 +225,25 @@ }, "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true } }, "@ethersproject/abi>@ethersproject/address": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/providers>@ethersproject/rlp": true } }, - "@ethersproject/abi>@ethersproject/bytes": { - "packages": { - "@ethersproject/abi>@ethersproject/logger": true - } - }, "@ethersproject/abi>@ethersproject/constants": { "packages": { "@ethersproject/bignumber": true @@ -256,7 +251,7 @@ }, "@ethersproject/abi>@ethersproject/keccak256": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@metamask/ethjs>js-sha3": true } }, @@ -272,18 +267,23 @@ }, "@ethersproject/abi>@ethersproject/strings": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@ethersproject/bignumber": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true, "bn.js": true } }, + "@ethersproject/bytes": { + "packages": { + "@ethersproject/abi>@ethersproject/logger": true + } + }, "@ethersproject/contracts": { "globals": { "setTimeout": true @@ -291,10 +291,10 @@ "packages": { "@ethersproject/abi": true, "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hdnode>@ethersproject/transactions": true, "@ethersproject/wallet>@ethersproject/abstract-provider": true @@ -303,12 +303,12 @@ "@ethersproject/hash": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/base64": true } }, @@ -324,16 +324,16 @@ "btoa": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true + "@ethersproject/bytes": true } }, "@ethersproject/hdnode": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/basex": true, "@ethersproject/hdnode>@ethersproject/pbkdf2": true, "@ethersproject/hdnode>@ethersproject/sha2": true, @@ -344,50 +344,50 @@ }, "@ethersproject/hdnode>@ethersproject/basex": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/properties": true + "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true } }, "@ethersproject/hdnode>@ethersproject/pbkdf2": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/sha2": true } }, "@ethersproject/hdnode>@ethersproject/sha2": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true, "ethereumjs-util>ethereum-cryptography>hash.js": true } }, "@ethersproject/hdnode>@ethersproject/signing-key": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true, "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/signing-key": true, "@ethersproject/providers>@ethersproject/rlp": true } }, "@ethersproject/hdnode>@ethersproject/wordlists": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true } }, @@ -403,12 +403,12 @@ }, "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hash>@ethersproject/base64": true, @@ -429,8 +429,8 @@ }, "@ethersproject/providers>@ethersproject/rlp": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@ethersproject/providers>@ethersproject/web": { @@ -440,20 +440,20 @@ "setTimeout": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/base64": true } }, "@ethersproject/wallet": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hdnode": true, @@ -466,20 +466,20 @@ }, "@ethersproject/wallet>@ethersproject/abstract-provider": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "@ethersproject/bignumber": true + "@ethersproject/bignumber": true, + "@ethersproject/bytes": true } }, "@ethersproject/wallet>@ethersproject/json-wallets": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode": true, "@ethersproject/hdnode>@ethersproject/pbkdf2": true, "@ethersproject/hdnode>@ethersproject/transactions": true, @@ -495,8 +495,8 @@ }, "@ethersproject/wallet>@ethersproject/random": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@keystonehq/bc-ur-registry-eth": { @@ -1525,9 +1525,9 @@ "setInterval": true }, "packages": { + "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller>@metamask/controller-utils": true, - "@metamask/gas-fee-controller>@metamask/polling-controller": true, "bn.js": true, "uuid": true } @@ -1550,18 +1550,6 @@ "eth-ens-namehash": true } }, - "@metamask/gas-fee-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true - } - }, "@metamask/jazzicon": { "globals": { "document.createElement": true, @@ -2206,7 +2194,7 @@ "setInterval": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/eth-query": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index daddd541a5f0..4506020c2bee 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -225,30 +225,25 @@ }, "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true } }, "@ethersproject/abi>@ethersproject/address": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/providers>@ethersproject/rlp": true } }, - "@ethersproject/abi>@ethersproject/bytes": { - "packages": { - "@ethersproject/abi>@ethersproject/logger": true - } - }, "@ethersproject/abi>@ethersproject/constants": { "packages": { "@ethersproject/bignumber": true @@ -256,7 +251,7 @@ }, "@ethersproject/abi>@ethersproject/keccak256": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@metamask/ethjs>js-sha3": true } }, @@ -272,18 +267,23 @@ }, "@ethersproject/abi>@ethersproject/strings": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@ethersproject/bignumber": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true, "bn.js": true } }, + "@ethersproject/bytes": { + "packages": { + "@ethersproject/abi>@ethersproject/logger": true + } + }, "@ethersproject/contracts": { "globals": { "setTimeout": true @@ -291,10 +291,10 @@ "packages": { "@ethersproject/abi": true, "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hdnode>@ethersproject/transactions": true, "@ethersproject/wallet>@ethersproject/abstract-provider": true @@ -303,12 +303,12 @@ "@ethersproject/hash": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/base64": true } }, @@ -324,16 +324,16 @@ "btoa": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true + "@ethersproject/bytes": true } }, "@ethersproject/hdnode": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/basex": true, "@ethersproject/hdnode>@ethersproject/pbkdf2": true, "@ethersproject/hdnode>@ethersproject/sha2": true, @@ -344,50 +344,50 @@ }, "@ethersproject/hdnode>@ethersproject/basex": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/properties": true + "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true } }, "@ethersproject/hdnode>@ethersproject/pbkdf2": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/sha2": true } }, "@ethersproject/hdnode>@ethersproject/sha2": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true, "ethereumjs-util>ethereum-cryptography>hash.js": true } }, "@ethersproject/hdnode>@ethersproject/signing-key": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true, "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/signing-key": true, "@ethersproject/providers>@ethersproject/rlp": true } }, "@ethersproject/hdnode>@ethersproject/wordlists": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true } }, @@ -403,12 +403,12 @@ }, "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hash>@ethersproject/base64": true, @@ -429,8 +429,8 @@ }, "@ethersproject/providers>@ethersproject/rlp": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@ethersproject/providers>@ethersproject/web": { @@ -440,20 +440,20 @@ "setTimeout": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/base64": true } }, "@ethersproject/wallet": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hdnode": true, @@ -466,20 +466,20 @@ }, "@ethersproject/wallet>@ethersproject/abstract-provider": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "@ethersproject/bignumber": true + "@ethersproject/bignumber": true, + "@ethersproject/bytes": true } }, "@ethersproject/wallet>@ethersproject/json-wallets": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode": true, "@ethersproject/hdnode>@ethersproject/pbkdf2": true, "@ethersproject/hdnode>@ethersproject/transactions": true, @@ -495,8 +495,8 @@ }, "@ethersproject/wallet>@ethersproject/random": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@keystonehq/bc-ur-registry-eth": { @@ -1525,9 +1525,9 @@ "setInterval": true }, "packages": { + "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller>@metamask/controller-utils": true, - "@metamask/gas-fee-controller>@metamask/polling-controller": true, "bn.js": true, "uuid": true } @@ -1550,18 +1550,6 @@ "eth-ens-namehash": true } }, - "@metamask/gas-fee-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true - } - }, "@metamask/jazzicon": { "globals": { "document.createElement": true, @@ -2206,7 +2194,7 @@ "setInterval": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/eth-query": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 0043b64f7534..bc5d084d0243 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -225,30 +225,25 @@ }, "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true } }, "@ethersproject/abi>@ethersproject/address": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/providers>@ethersproject/rlp": true } }, - "@ethersproject/abi>@ethersproject/bytes": { - "packages": { - "@ethersproject/abi>@ethersproject/logger": true - } - }, "@ethersproject/abi>@ethersproject/constants": { "packages": { "@ethersproject/bignumber": true @@ -256,7 +251,7 @@ }, "@ethersproject/abi>@ethersproject/keccak256": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@metamask/ethjs>js-sha3": true } }, @@ -272,18 +267,23 @@ }, "@ethersproject/abi>@ethersproject/strings": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@ethersproject/bignumber": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true, "bn.js": true } }, + "@ethersproject/bytes": { + "packages": { + "@ethersproject/abi>@ethersproject/logger": true + } + }, "@ethersproject/contracts": { "globals": { "setTimeout": true @@ -291,10 +291,10 @@ "packages": { "@ethersproject/abi": true, "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hdnode>@ethersproject/transactions": true, "@ethersproject/wallet>@ethersproject/abstract-provider": true @@ -303,12 +303,12 @@ "@ethersproject/hash": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/base64": true } }, @@ -324,16 +324,16 @@ "btoa": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true + "@ethersproject/bytes": true } }, "@ethersproject/hdnode": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/basex": true, "@ethersproject/hdnode>@ethersproject/pbkdf2": true, "@ethersproject/hdnode>@ethersproject/sha2": true, @@ -344,50 +344,50 @@ }, "@ethersproject/hdnode>@ethersproject/basex": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/properties": true + "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true } }, "@ethersproject/hdnode>@ethersproject/pbkdf2": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/sha2": true } }, "@ethersproject/hdnode>@ethersproject/sha2": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true, "ethereumjs-util>ethereum-cryptography>hash.js": true } }, "@ethersproject/hdnode>@ethersproject/signing-key": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true, "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode>@ethersproject/signing-key": true, "@ethersproject/providers>@ethersproject/rlp": true } }, "@ethersproject/hdnode>@ethersproject/wordlists": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true } }, @@ -403,12 +403,12 @@ }, "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/constants": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, "@ethersproject/bignumber": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hash>@ethersproject/base64": true, @@ -429,8 +429,8 @@ }, "@ethersproject/providers>@ethersproject/rlp": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@ethersproject/providers>@ethersproject/web": { @@ -440,20 +440,20 @@ "setTimeout": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hash>@ethersproject/base64": true } }, "@ethersproject/wallet": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, + "@ethersproject/bytes": true, "@ethersproject/hash": true, "@ethersproject/hash>@ethersproject/abstract-signer": true, "@ethersproject/hdnode": true, @@ -466,20 +466,20 @@ }, "@ethersproject/wallet>@ethersproject/abstract-provider": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "@ethersproject/bignumber": true + "@ethersproject/bignumber": true, + "@ethersproject/bytes": true } }, "@ethersproject/wallet>@ethersproject/json-wallets": { "packages": { "@ethersproject/abi>@ethersproject/address": true, - "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/keccak256": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, "@ethersproject/abi>@ethersproject/strings": true, + "@ethersproject/bytes": true, "@ethersproject/hdnode": true, "@ethersproject/hdnode>@ethersproject/pbkdf2": true, "@ethersproject/hdnode>@ethersproject/transactions": true, @@ -495,8 +495,8 @@ }, "@ethersproject/wallet>@ethersproject/random": { "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, - "@ethersproject/abi>@ethersproject/logger": true + "@ethersproject/abi>@ethersproject/logger": true, + "@ethersproject/bytes": true } }, "@keystonehq/bc-ur-registry-eth": { @@ -1617,9 +1617,9 @@ "setInterval": true }, "packages": { + "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller>@metamask/controller-utils": true, - "@metamask/gas-fee-controller>@metamask/polling-controller": true, "bn.js": true, "uuid": true } @@ -1642,18 +1642,6 @@ "eth-ens-namehash": true } }, - "@metamask/gas-fee-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true - } - }, "@metamask/jazzicon": { "globals": { "document.createElement": true, @@ -2298,7 +2286,7 @@ "setInterval": true }, "packages": { - "@ethersproject/abi>@ethersproject/bytes": true, + "@ethersproject/bytes": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/eth-query": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, diff --git a/package.json b/package.json index 4d00099109c4..1a9086d623e4 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,8 @@ "dapp-chain": "GANACHE_ARGS='-b 2' concurrently -k -n ganache,dapp -p '[{time}][{name}]' 'yarn ganache:start' 'sleep 5 && yarn dapp'", "forwarder": "node ./development/static-server.js ./node_modules/@metamask/forwarder/dist/ --port 9010", "dapp-forwarder": "concurrently -k -n forwarder,dapp -p '[{time}][{name}]' 'yarn forwarder' 'yarn dapp'", + "test:integration": "jest --config jest.integration.config.js", + "test:integration:coverage": "jest --config jest.integration.config.js --coverage", "test:unit": "node ./test/run-unit-tests.js --jestGlobal --jestDev", "test:unit:jest": "node ./test/run-unit-tests.js --jestGlobal --jestDev", "test:unit:jest:watch": "node --inspect ./node_modules/.bin/jest --watch", @@ -255,7 +257,6 @@ "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A19.0.0#~/.yarn/patches/@metamask-network-controller-npm-19.0.0-a5e0d1fe14.patch", "@solana/web3.js/rpc-websockets": "^8.0.1", "@metamask/network-controller@npm:^19.0.0": "patch:@metamask/network-controller@npm%3A19.0.0#~/.yarn/patches/@metamask-network-controller-npm-19.0.0-a5e0d1fe14.patch", - "@metamask/gas-fee-controller@npm:^15.1.1": "patch:@metamask/gas-fee-controller@npm%3A15.1.2#~/.yarn/patches/@metamask-gas-fee-controller-npm-15.1.2-db4d2976aa.patch", "@metamask/nonce-tracker@npm:^5.0.0": "patch:@metamask/nonce-tracker@npm%3A5.0.0#~/.yarn/patches/@metamask-nonce-tracker-npm-5.0.0-d81478218e.patch" }, "dependencies": { @@ -266,6 +267,7 @@ "@ethereumjs/tx": "^4.1.1", "@ethersproject/abi": "^5.6.4", "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", "@ethersproject/contracts": "^5.7.0", "@ethersproject/hash": "^5.7.0", "@ethersproject/hdnode": "^5.6.2", @@ -286,13 +288,13 @@ "@metamask-institutional/sdk": "^0.1.30", "@metamask-institutional/transaction-update": "^0.2.5", "@metamask/abi-utils": "^2.0.2", - "@metamask/accounts-controller": "^17.0.0", + "@metamask/accounts-controller": "^17.2.0", "@metamask/address-book-controller": "^4.0.1", "@metamask/announcement-controller": "^6.1.0", "@metamask/approval-controller": "^7.0.0", "@metamask/assets-controllers": "^34.0.0", "@metamask/base-controller": "^5.0.1", - "@metamask/bitcoin-wallet-snap": "^0.2.2", + "@metamask/bitcoin-wallet-snap": "^0.2.3", "@metamask/browser-passworder": "^4.3.0", "@metamask/contract-metadata": "^2.5.0", "@metamask/controller-utils": "^10.0.0", @@ -310,7 +312,7 @@ "@metamask/ethjs": "^0.6.0", "@metamask/ethjs-contract": "^0.4.1", "@metamask/ethjs-query": "^0.7.1", - "@metamask/gas-fee-controller": "patch:@metamask/gas-fee-controller@npm%3A15.1.2#~/.yarn/patches/@metamask-gas-fee-controller-npm-15.1.2-db4d2976aa.patch", + "@metamask/gas-fee-controller": "^18.0.0", "@metamask/jazzicon": "^2.0.0", "@metamask/keyring-api": "^8.0.0", "@metamask/keyring-controller": "^16.1.0", diff --git a/privacy-snapshot.json b/privacy-snapshot.json index fe6579bfab73..2fff74ee56fe 100644 --- a/privacy-snapshot.json +++ b/privacy-snapshot.json @@ -38,6 +38,7 @@ "responsive-rpc.test", "sentry.io", "snaps.metamask.io", + "sourcify.dev", "start.metamask.io", "static.cx.metamask.io", "swap.api.cx.metamask.io", diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 209cc3c7f414..7a83ec4f61e2 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -512,6 +512,7 @@ export enum MetaMetricsEventName { AppLocked = 'App Locked', AppWindowExpanded = 'App Window Expanded', BridgeLinkClicked = 'Bridge Link Clicked', + BitcoinSupportToggled = 'Bitcoin Support Toggled', DappViewed = 'Dapp Viewed', DecryptionApproved = 'Decryption Approved', DecryptionRejected = 'Decryption Rejected', diff --git a/shared/lib/four-byte.test.ts b/shared/lib/four-byte.test.ts new file mode 100644 index 000000000000..ab4e60fd26e2 --- /dev/null +++ b/shared/lib/four-byte.test.ts @@ -0,0 +1,28 @@ +import { + FOUR_BYTE_RESPONSE, + TRANSACTION_DATA_FOUR_BYTE, +} from '../../test/data/confirmations/transaction-decode'; +import { getMethodFrom4Byte } from './four-byte'; + +const FOUR_BYTE_MOCK = TRANSACTION_DATA_FOUR_BYTE.slice(0, 10); + +describe('Four Byte', () => { + const fetchMock = jest.fn(); + + beforeEach(() => { + jest.spyOn(global, 'fetch').mockImplementation(fetchMock); + }); + + describe('getMethodFrom4Byte', () => { + it('returns signature with earliest creation date', async () => { + fetchMock.mockResolvedValue({ + ok: true, + json: async () => FOUR_BYTE_RESPONSE, + }); + + const result = await getMethodFrom4Byte(FOUR_BYTE_MOCK); + + expect(result).toStrictEqual('someOtherFunction(address,uint256)'); + }); + }); +}); diff --git a/shared/lib/four-byte.ts b/shared/lib/four-byte.ts new file mode 100644 index 000000000000..f52b3ff6e8c6 --- /dev/null +++ b/shared/lib/four-byte.ts @@ -0,0 +1,33 @@ +import fetchWithCache from './fetch-with-cache'; + +type FourByteResult = { + created_at: string; + text_signature: string; +}; + +type FourByteResponse = { + results: FourByteResult[]; +}; + +export async function getMethodFrom4Byte( + fourBytePrefix: string, +): Promise { + const fourByteResponse = (await fetchWithCache({ + url: `https://www.4byte.directory/api/v1/signatures/?hex_signature=${fourBytePrefix}`, + fetchOptions: { + referrerPolicy: 'no-referrer-when-downgrade', + body: null, + method: 'GET', + mode: 'cors', + }, + functionName: 'getMethodFrom4Byte', + })) as FourByteResponse; + + fourByteResponse.results.sort((a, b) => { + return new Date(a.created_at).getTime() < new Date(b.created_at).getTime() + ? -1 + : 1; + }); + + return fourByteResponse.results[0].text_signature; +} diff --git a/shared/types/transaction-decode.ts b/shared/types/transaction-decode.ts new file mode 100644 index 000000000000..d76c03b1a878 --- /dev/null +++ b/shared/types/transaction-decode.ts @@ -0,0 +1,25 @@ +export enum DecodedTransactionDataSource { + Uniswap = 'Uniswap', + Sourcify = 'Sourcify', + FourByte = 'FourByte', +} + +export type DecodedTransactionDataResponse = { + data: DecodedTransactionDataMethod[]; + source: DecodedTransactionDataSource; +}; + +export type DecodedTransactionDataMethod = { + name: string; + description?: string; + params: DecodedTransactionDataParam[]; +}; + +export type DecodedTransactionDataParam = { + name?: string; + description?: string; + type: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any; + children?: DecodedTransactionDataParam[]; +}; diff --git a/test/data/confirmations/transaction-decode.ts b/test/data/confirmations/transaction-decode.ts new file mode 100644 index 000000000000..e9700ababff3 --- /dev/null +++ b/test/data/confirmations/transaction-decode.ts @@ -0,0 +1,604 @@ +import { DecodedTransactionDataSource } from '../../../shared/types/transaction-decode'; + +export const CONTRACT_ADDRESS_UNISWAP = '0x1'; +export const CONTRACT_ADDRESS_SOURCIFY = '0x2'; +export const CONTRACT_ADDRESS_FOUR_BYTE = '0x3'; + +export const TRANSACTION_DATA_UNISWAP = + '0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006679b4bb00000000000000000000000000000000000000000000000000000000000000040b000604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000005af3107a40000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000005af3107a4000000000000000000000000000000000000000000000000000000000000004c41800000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000027213e28d7fda5c57fe9e5dd923818dbccf71c4700000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000060000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000004c418'; + +export const TRANSACTION_DATA_SOURCIFY = + '0xa9059cbb000000000000000000000000ec8507ecf7e946992294f06423a79835a32268460000000000000000000000000000000000000000000000000000000000000064'; + +export const TRANSACTION_DATA_SOURCIFY_NESTED = + '0x2a2d80d1000000000000000000000000be3be93ffad7d417c08124b43286f4476c006afe000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000cc97f2e548ab94f40e5adf473f596ced83b6ee0a0000000000000000000000000000000000000000000000000000019747f66fc300000000000000000000000000000000000000000000000000000000000000030000000000000000000000000305f515fa978cf87226cf8a9776d25bcfb2cc0b000000000000000000000000ffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000019747f66fc300000000000000000000000000000000000000000000000000000000000000000000000000000000000000004385328cc4d643ca98dfea734360c0f596c83449000000000000000000000000ffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000019747f66fc30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000ffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000019747f66fc30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004156e1fabfaf96d309b0039896d1f68b51e27f0e25b4481db8cad059b7e8db95d918bdc43ab1d03a0b6a84c7ea2219bf01133ceab4e7ca4e38055e4ed8af78a63b1b00000000000000000000000000000000000000000000000000000000000000'; + +export const TRANSACTION_DATA_FOUR_BYTE = + '0x12345678000000000000000000000000ec8507ecf7e946992294f06423a79835a32268460000000000000000000000000000000000000000000000000000000000000064'; + +export const TRANSACTION_DECODE_UNISWAP = { + source: DecodedTransactionDataSource.Uniswap, + data: [ + { + name: 'WRAP_ETH', + params: [ + { + name: 'recipient', + type: 'address', + value: '0x0000000000000000000000000000000000000002', + description: 'The recipient of the WETH', + children: undefined, + }, + { + name: 'amountMin', + type: 'uint256', + value: 123456, + description: 'The amount of ETH to wrap', + children: undefined, + }, + ], + }, + { + name: 'V3_SWAP_EXACT_IN', + params: [ + { + name: 'recipient', + type: 'address', + value: '0x0000000000000000000000000000000000000002', + description: 'The recipient of the output of the trade', + children: undefined, + }, + { + name: 'amountIn', + type: 'uint256', + value: 123456, + description: 'The amount of input tokens for the trade', + children: undefined, + }, + { + name: 'amountOutMin', + type: 'uint256', + value: 123456, + description: 'The minimum amount of output tokens the user wants', + children: undefined, + }, + { + name: 'path', + type: 'bytes', + value: [ + { + firstAddress: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + tickSpacing: 500, + secondAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + }, + { + firstAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + tickSpacing: 100, + secondAddress: '0xd02aaa39b223fe8d0a0e5c4f27ead9083c756cc3', + }, + ], + description: 'The UniswapV3 encoded path to trade along', + children: undefined, + }, + { + name: 'payerIsUser', + type: 'bool', + value: false, + description: + 'A flag for whether the input tokens should come from the msg.sender (through Permit2) or whether the funds are already in the UniversalRouter', + children: undefined, + }, + ], + }, + { + name: 'PAY_PORTION', + params: [ + { + name: 'token', + type: 'address', + value: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + description: 'The ERC20 token to transfer (or Constants.ETH for ETH)', + children: undefined, + }, + { + name: 'recipient', + type: 'address', + value: '0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47', + description: 'The recipient of the transfer', + children: undefined, + }, + { + name: 'bips', + type: 'uint256', + value: 123456, + description: + 'In basis points, the percentage of the contract’s balance to transfer', + children: undefined, + }, + ], + }, + { + name: 'SWEEP', + params: [ + { + name: 'token', + type: 'address', + value: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + description: 'The ERC20 token to sweep (or Constants.ETH for ETH)', + children: undefined, + }, + { + name: 'recipient', + type: 'address', + value: '0x0000000000000000000000000000000000000001', + description: 'The recipient of the sweep', + children: undefined, + }, + { + name: 'amountMin', + type: 'uint256', + value: 123456, + description: 'The minimum required tokens to receive from the sweep', + children: undefined, + }, + ], + }, + ], +}; + +export const TRANSACTION_DECODE_SOURCIFY = { + source: DecodedTransactionDataSource.Sourcify, + data: [ + { + name: 'cancelAuthorization', + description: 'Attempt to cancel an authorization', + params: [ + { + name: 'authorizer', + description: "Authorizer's address", + type: 'address', + value: '0xB0dA5965D43369968574D399dBe6374683773a65', + children: undefined, + }, + { + name: 'nonce', + description: 'Nonce of the authorization', + type: 'bytes32', + value: + '0x0000000000000000000000000000000000000000000000000000000000000123', + children: undefined, + }, + { + name: 'signature', + description: + 'Signature bytes signed by an EOA wallet or a contract wallet', + type: 'bytes', + value: '0x0456', + children: undefined, + }, + ], + }, + ], +}; + +export const TRANSACTION_DECODE_FOUR_BYTE = { + source: DecodedTransactionDataSource.FourByte, + data: [ + { + name: 'someFunction', + params: [ + { + children: undefined, + type: 'uint256', + value: 123456, + }, + { + children: undefined, + type: 'address', + value: '0x1234567890123456789012345678901234567890', + }, + { + children: undefined, + type: 'bytes', + value: '0x123', + }, + ], + }, + ], +}; + +export const TRANSACTION_DECODE_NESTED = { + source: DecodedTransactionDataSource.Sourcify, + data: [ + { + description: + "Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature", + name: 'permit', + params: [ + { + children: undefined, + description: 'The owner of the tokens being approved', + name: 'owner', + type: 'address', + value: '0xBe3be93fFAD7d417C08124B43286f4476C006AFe', + }, + { + children: [ + { + children: [ + { + children: [ + { + children: undefined, + description: undefined, + name: 'token', + type: 'address', + value: '0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B', + }, + { + children: undefined, + description: undefined, + name: 'amount', + type: 'uint160', + value: '0xffffffffffffffffffffffffffffffffffffffff', + }, + { + children: undefined, + description: undefined, + name: 'expiration', + type: 'uint48', + value: 1749259022275, + }, + { + children: undefined, + description: undefined, + name: 'nonce', + type: 'uint48', + value: 0, + }, + ], + description: undefined, + name: 'Item 1', + type: 'tuple', + value: [ + '0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B', + '0xffffffffffffffffffffffffffffffffffffffff', + 1749259022275, + 0, + ], + }, + { + children: [ + { + children: undefined, + description: undefined, + name: 'token', + type: 'address', + value: '0x4385328cc4D643Ca98DfEA734360C0F596C83449', + }, + { + children: undefined, + description: undefined, + name: 'amount', + type: 'uint160', + value: '0xffffffffffffffffffffffffffffffffffffffff', + }, + { + children: undefined, + description: undefined, + name: 'expiration', + type: 'uint48', + value: 1749259022275, + }, + { + children: undefined, + description: undefined, + name: 'nonce', + type: 'uint48', + value: 0, + }, + ], + description: undefined, + name: 'Item 2', + type: 'tuple', + value: [ + '0x4385328cc4D643Ca98DfEA734360C0F596C83449', + '0xffffffffffffffffffffffffffffffffffffffff', + 1749259022275, + 0, + ], + }, + { + children: [ + { + children: undefined, + description: undefined, + name: 'token', + type: 'address', + value: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + }, + { + children: undefined, + description: undefined, + name: 'amount', + type: 'uint160', + value: '0xffffffffffffffffffffffffffffffffffffffff', + }, + { + children: undefined, + description: undefined, + name: 'expiration', + type: 'uint48', + value: 1749259022275, + }, + { + children: undefined, + description: undefined, + name: 'nonce', + type: 'uint48', + value: 0, + }, + ], + description: undefined, + name: 'Item 3', + type: 'tuple', + value: [ + '0xdAC17F958D2ee523a2206206994597C13D831ec7', + '0xffffffffffffffffffffffffffffffffffffffff', + 1749259022275, + 0, + ], + }, + ], + description: undefined, + name: 'details', + type: 'tuple[]', + value: [ + [ + '0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B', + '0xffffffffffffffffffffffffffffffffffffffff', + 1749259022275, + 0, + ], + [ + '0x4385328cc4D643Ca98DfEA734360C0F596C83449', + '0xffffffffffffffffffffffffffffffffffffffff', + 1749259022275, + 0, + ], + [ + '0xdAC17F958D2ee523a2206206994597C13D831ec7', + '0xffffffffffffffffffffffffffffffffffffffff', + 1749259022275, + 0, + ], + ], + }, + { + children: undefined, + description: undefined, + name: 'spender', + type: 'address', + value: '0xCC97F2E548ab94F40e5ADf473F596CEd83B6ee0a', + }, + { + children: undefined, + description: undefined, + name: 'sigDeadline', + type: 'uint256', + value: '0x019747f66fc3', + }, + ], + description: + 'Data signed over by the owner specifying the terms of approval', + name: 'permitBatch', + type: 'tuple', + value: [ + [ + [ + '0x0305f515fa978cf87226cf8A9776D25bcfb2Cc0B', + '0xffffffffffffffffffffffffffffffffffffffff', + 1749259022275, + 0, + ], + [ + '0x4385328cc4D643Ca98DfEA734360C0F596C83449', + '0xffffffffffffffffffffffffffffffffffffffff', + 1749259022275, + 0, + ], + [ + '0xdAC17F958D2ee523a2206206994597C13D831ec7', + '0xffffffffffffffffffffffffffffffffffffffff', + 1749259022275, + 0, + ], + ], + '0xCC97F2E548ab94F40e5ADf473F596CEd83B6ee0a', + '0x019747f66fc3', + ], + }, + { + children: undefined, + description: "The owner's signature over the permit data", + name: 'signature', + type: 'bytes', + value: + '0x56e1fabfaf96d309b0039896d1f68b51e27f0e25b4481db8cad059b7e8db95d918bdc43ab1d03a0b6a84c7ea2219bf01133ceab4e7ca4e38055e4ed8af78a63b1b', + }, + ], + }, + ], +}; + +export const SOURCIFY_RESPONSE = { + files: [ + { + name: 'metadata.json', + content: JSON.stringify({ + output: { + abi: [ + { + constant: false, + inputs: [ + { + name: 'to', + type: 'address', + }, + { + name: 'value', + type: 'uint256', + }, + ], + name: 'transfer', + outputs: [ + { + name: 'success', + type: 'bool', + }, + ], + payable: false, + type: 'function', + }, + ], + userdoc: { + methods: { + 'transfer(address,uint256)': { + notice: 'Transfer tokens', + params: { + to: 'The address to transfer to', + value: 'The amount to transfer', + }, + }, + }, + }, + }, + }), + }, + ], +}; + +export const SOURCIFY_RESPONSE_NESTED = { + files: [ + { + name: 'metadata.json', + content: JSON.stringify({ + output: { + abi: [ + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + components: [ + { + components: [ + { + internalType: 'address', + name: 'token', + type: 'address', + }, + { + internalType: 'uint160', + name: 'amount', + type: 'uint160', + }, + { + internalType: 'uint48', + name: 'expiration', + type: 'uint48', + }, + { + internalType: 'uint48', + name: 'nonce', + type: 'uint48', + }, + ], + internalType: 'struct IAllowanceTransfer.PermitDetails[]', + name: 'details', + type: 'tuple[]', + }, + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'sigDeadline', + type: 'uint256', + }, + ], + internalType: 'struct IAllowanceTransfer.PermitBatch', + name: 'permitBatch', + type: 'tuple', + }, + { + internalType: 'bytes', + name: 'signature', + type: 'bytes', + }, + ], + name: 'permit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + ], + devdoc: { + methods: { + 'permit(address,((address,uint160,uint48,uint48)[],address,uint256),bytes)': + { + details: + "May fail if the owner's nonce was invalidated in-flight by invalidateNonce", + params: { + owner: 'The owner of the tokens being approved', + permitBatch: + 'Data signed over by the owner specifying the terms of approval', + signature: "The owner's signature over the permit data", + }, + }, + }, + }, + userdoc: { + methods: { + 'permit(address,((address,uint160,uint48,uint48)[],address,uint256),bytes)': + { + notice: + "Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature", + }, + }, + notice: + 'Permit2 handles signature-based transfers in SignatureTransfer and allowance-based transfers in AllowanceTransfer.', + }, + }, + }), + }, + ], +}; + +export const FOUR_BYTE_RESPONSE = { + results: [ + { + created_at: '2022-09-01T00:00:00.000Z', + text_signature: 'someFunction(address,uint256)', + }, + { + created_at: '2021-09-01T00:00:00.000Z', + text_signature: 'someOtherFunction(address,uint256)', + }, + ], +}; + +export const FOUR_BYTE_RESPONSE_NESTED = { + results: [ + { + created_at: '2022-09-01T00:00:00.000Z', + text_signature: + 'permit(address,((address,uint160,uint48,uint48)[],address,uint256),bytes)', + }, + ], +}; diff --git a/test/data/mock-state.json b/test/data/mock-state.json index 9e595ad8f0e0..6ec7a271f2c1 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -532,6 +532,7 @@ }, "selectedAccount": "cf8dace4-9439-4bd4-b3a8-88c821c8fcb3" }, + "balances": {}, "keyrings": [ { "type": "HD Key Tree", @@ -1879,6 +1880,7 @@ } ], "addSnapAccountEnabled": false, + "bitcoinSupportEnabled": false, "pendingApprovals": { "testApprovalId": { "id": "testApprovalId", diff --git a/test/e2e/accounts/snap-account-settings.spec.ts b/test/e2e/accounts/snap-account-settings.spec.ts index 162fae62ec7e..5a27f10f4764 100644 --- a/test/e2e/accounts/snap-account-settings.spec.ts +++ b/test/e2e/accounts/snap-account-settings.spec.ts @@ -26,7 +26,12 @@ describe('Add snap account experimental settings', function (this: Suite) { text: 'Add account Snap', tag: 'button', }, - { findElementGuard: { text: 'Add a new account', tag: 'button' } }, // wait for the modal to appear + { + findElementGuard: { + text: 'Add a new Ethereum account', + tag: 'button', + }, + }, // wait for the modal to appear ); await driver.clickElement('.mm-box button[aria-label="Close"]'); diff --git a/test/e2e/json-rpc/switchEthereumChain.spec.js b/test/e2e/json-rpc/switchEthereumChain.spec.js index ef195f4b9a2b..f8ccb0116e84 100644 --- a/test/e2e/json-rpc/switchEthereumChain.spec.js +++ b/test/e2e/json-rpc/switchEthereumChain.spec.js @@ -50,7 +50,9 @@ describe('Switch Ethereum Chain for two dapps', function () { await driver.clickElement(experimentalTabRawLocator); // Toggle off request queue setting (on by default now) - await driver.clickElement('.request-queue-toggle'); + await driver.clickElement( + '[data-testid="experimental-setting-toggle-request-queue"]', + ); // open two dapps const dappOne = await openDapp(driver, undefined, DAPP_URL); @@ -99,7 +101,7 @@ describe('Switch Ethereum Chain for two dapps', function () { ); }); - it('should queue switchEthereumChain request from second dapp after send tx request', async function () { + it('queues switchEthereumChain request from second dapp after send tx request', async function () { await withFixtures( { dapp: true, @@ -137,7 +139,9 @@ describe('Switch Ethereum Chain for two dapps', function () { await driver.clickElement(experimentalTabRawLocator); // Toggle off request queue setting (on by default now) - await driver.clickElement('.request-queue-toggle'); + await driver.clickElement( + '[data-testid="experimental-setting-toggle-request-queue"]', + ); // open two dapps const dappOne = await openDapp(driver, undefined, DAPP_URL); @@ -189,7 +193,7 @@ describe('Switch Ethereum Chain for two dapps', function () { ); }); - it('should queue send tx after switchEthereum request with a warning, confirming removes pending tx', async function () { + it('queues send tx after switchEthereum request with a warning, confirming removes pending tx', async function () { await withFixtures( { dapp: true, @@ -227,7 +231,9 @@ describe('Switch Ethereum Chain for two dapps', function () { await driver.clickElement(experimentalTabRawLocator); // Toggle off request queue setting (on by default now) - await driver.clickElement('.request-queue-toggle'); + await driver.clickElement( + '[data-testid="experimental-setting-toggle-request-queue"]', + ); // open two dapps const dappOne = await openDapp(driver, undefined, DAPP_URL); @@ -286,7 +292,7 @@ describe('Switch Ethereum Chain for two dapps', function () { ); }); - it('should queue send tx after switchEthereum request with a warning, if switchEthereum request is cancelled should show pending tx', async function () { + it('queues send tx after switchEthereum request with a warning, if switchEthereum request is cancelled should show pending tx', async function () { await withFixtures( { dapp: true, @@ -324,7 +330,9 @@ describe('Switch Ethereum Chain for two dapps', function () { await driver.clickElement(experimentalTabRawLocator); // Toggle off request queue setting (on by default now) - await driver.clickElement('.request-queue-toggle'); + await driver.clickElement( + '[data-testid="experimental-setting-toggle-request-queue"]', + ); // open two dapps const dappOne = await openDapp(driver, undefined, DAPP_URL); diff --git a/test/e2e/mock-e2e.js b/test/e2e/mock-e2e.js index 50bd7633f8d3..506331e1ac21 100644 --- a/test/e2e/mock-e2e.js +++ b/test/e2e/mock-e2e.js @@ -619,6 +619,12 @@ async function setupMocking( // Notification APIs mockNotificationServices(server); + await server.forGet(/^https:\/\/sourcify.dev\/(.*)/u).thenCallback(() => { + return { + statusCode: 404, + }; + }); + /** * Returns an array of alphanumerically sorted hostnames that were requested * during the current test suite. diff --git a/test/e2e/snaps/test-snap-metrics.spec.js b/test/e2e/snaps/test-snap-metrics.spec.js index 8c5978f28609..bd414c209402 100644 --- a/test/e2e/snaps/test-snap-metrics.spec.js +++ b/test/e2e/snaps/test-snap-metrics.spec.js @@ -589,6 +589,12 @@ describe('Test Snap Metrics', function () { await driver.waitForSelector({ text: 'Confirm' }); + // Wait for the permissions content to be rendered + await driver.waitForSelector({ + text: 'Bitcoin Legacy', + tag: 'span', + }); + await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); await driver.clickElement({ @@ -733,6 +739,12 @@ describe('Test Snap Metrics', function () { await driver.waitForSelector({ text: 'Confirm' }); + // Wait for the permissions content to be rendered + await driver.waitForSelector({ + text: 'Bitcoin Legacy', + tag: 'span', + }); + await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); await driver.clickElement({ @@ -863,6 +875,12 @@ describe('Test Snap Metrics', function () { await driver.waitForSelector({ text: 'Confirm' }); + // Wait for the permissions content to be rendered + await driver.waitForSelector({ + text: 'Bitcoin Legacy', + tag: 'span', + }); + await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); await driver.clickElement({ diff --git a/test/e2e/tests/account/import-flow.spec.js b/test/e2e/tests/account/import-flow.spec.js index e25174cfa249..74ac0731086f 100644 --- a/test/e2e/tests/account/import-flow.spec.js +++ b/test/e2e/tests/account/import-flow.spec.js @@ -101,7 +101,10 @@ describe('Import flow @no-mmi', function () { await driver.clickElement( '[data-testid="multichain-account-menu-popover-action-button"]', ); - await driver.clickElement({ text: 'Add a new account', tag: 'button' }); + await driver.clickElement({ + text: 'Add a new Ethereum account', + tag: 'button', + }); // set account name await driver.fill('[placeholder="Account 2"]', '2nd account'); diff --git a/test/e2e/tests/confirmations/header.spec.js b/test/e2e/tests/confirmations/header.spec.js index c829958f8b5e..91828147b4ab 100644 --- a/test/e2e/tests/confirmations/header.spec.js +++ b/test/e2e/tests/confirmations/header.spec.js @@ -86,12 +86,14 @@ describe('Confirmation Header Component', function () { async function clickHeaderInfoBtn(driver) { await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - await driver.clickElement('button[data-testid="header-info-button"]'); + await driver.clickElement( + 'button[data-testid="header-info__account-details-button"]', + ); } async function assertHeaderInfoBalance(driver, walletEthBalance) { const headerBalanceEl = await driver.findElement( - '[data-testid="header-balance"]', + '[data-testid="confirmation-account-details-modal__account-balance"]', ); await driver.waitForNonEmptyElement(headerBalanceEl); assert.equal(await headerBalanceEl.getText(), `${walletEthBalance}\nETH`); diff --git a/test/e2e/tests/confirmations/navigation.spec.ts b/test/e2e/tests/confirmations/navigation.spec.ts new file mode 100644 index 000000000000..32d58fdbc334 --- /dev/null +++ b/test/e2e/tests/confirmations/navigation.spec.ts @@ -0,0 +1,210 @@ +import { strict as assert } from 'assert'; +import { Suite } from 'mocha'; +import { + DAPP_HOST_ADDRESS, + WINDOW_TITLES, + openDapp, + unlockWallet, + regularDelayMs, +} from '../../helpers'; +import { Driver } from '../../webdriver/driver'; +import { withRedesignConfirmationFixtures } from './helpers'; + +describe('Navigation Signature - Different signature types', function (this: Suite) { + if (!process.env.ENABLE_CONFIRMATION_REDESIGN) { + return; + } + + it('initiates and queues multiple signatures and confirms', async function () { + await withRedesignConfirmationFixtures( + this.test?.fullTitle(), + async ({ driver }: { driver: Driver }) => { + await unlockWallet(driver); + await openDapp(driver); + await queueSignatures(driver); + + await verifySignTypedData(driver); + await driver.clickElement( + '[data-testid="confirm-nav__next-confirmation"]', + ); + + // Verify Sign Typed Data v3 confirmation is displayed + await verifySignedTypeV3Confirmation(driver); + + await driver.clickElement( + '[data-testid="confirm-nav__next-confirmation"]', + ); + + // Verify Sign Typed Data v4 confirmation is displayed + await verifySignedTypeV4Confirmation(driver); + + await driver.clickElement( + '[data-testid="confirm-nav__previous-confirmation"]', + ); + + // Verify Sign Typed Data v3 confirmation is displayed + await verifySignedTypeV3Confirmation(driver); + + await driver.clickElement( + '[data-testid="confirm-nav__previous-confirmation"]', + ); + // Verify Sign Typed Data v3 confirmation is displayed + await verifySignTypedData(driver); + }, + ); + }); + + it('initiates and queues a mix of signatures and transactions and navigates', async function () { + await withRedesignConfirmationFixtures( + this.test?.fullTitle(), + async ({ driver }: { driver: Driver }) => { + await unlockWallet(driver); + await openDapp(driver); + await queueSignaturesAndTransactions(driver); + + await verifySignTypedData(driver); + + await driver.clickElement( + '[data-testid="confirm-nav__next-confirmation"]', + ); + + // Verify Transaction Sending ETH is displayed + await verifyTransaction(driver, 'SENDING ETH'); + + await driver.clickElement('[data-testid="next-page"]'); + + // Verify Sign Typed Data v3 confirmation is displayed + await verifySignedTypeV3Confirmation(driver); + + await driver.clickElement( + '[data-testid="confirm-nav__previous-confirmation"]', + ); + + // Verify Sign Typed Data v3 confirmation is displayed + await verifyTransaction(driver, 'SENDING ETH'); + + await driver.clickElement('[data-testid="previous-page"]'); + + // Verify Sign Typed Data v3 confirmation is displayed + await verifySignTypedData(driver); + }, + ); + }); + + it('initiates multiple signatures and rejects all', async function () { + await withRedesignConfirmationFixtures( + this.test?.fullTitle(), + async ({ driver }: { driver: Driver }) => { + await unlockWallet(driver); + await openDapp(driver); + await queueSignatures(driver); + await driver.delay(regularDelayMs); + + await driver.clickElement('[data-testid="confirm-nav__reject-all"]'); + + await driver.waitUntilXWindowHandles(2); + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + await verifyRejectionResults(driver, '#signTypedDataResult'); + await verifyRejectionResults(driver, '#signTypedDataV3Result'); + await verifyRejectionResults(driver, '#signTypedDataV4Result'); + }, + ); + }); +}); + +async function verifySignTypedData(driver: Driver) { + const origin = await driver.findElement({ text: DAPP_HOST_ADDRESS }); + const message = await driver.findElement({ text: 'Hi, Alice!' }); + + // Verify Sign Typed Data confirmation is displayed + assert.ok(origin, 'origin'); + assert.ok(message, 'message'); +} + +async function verifyRejectionResults(driver: Driver, verifyResultId: string) { + const rejectionResult = await driver.findElement(verifyResultId); + assert.equal( + await rejectionResult.getText(), + 'Error: User rejected the request.', + ); +} + +async function verifySignedTypeV3Confirmation(driver: Driver) { + const origin = await driver.findElement({ text: DAPP_HOST_ADDRESS }); + const fromAddress = driver.findElement({ + css: '.name__value', + text: '0xCD2a3...DD826', + }); + const toAddress = driver.findElement({ + css: '.name__value', + text: '0xbBbBB...bBBbB', + }); + const contents = driver.findElement({ text: 'Hello, Bob!' }); + + assert.ok(await origin, 'origin'); + assert.ok(await fromAddress, 'fromAddress'); + assert.ok(await toAddress, 'toAddress'); + assert.ok(await contents, 'contents'); +} + +async function verifySignedTypeV4Confirmation(driver: Driver) { + verifySignedTypeV3Confirmation(driver); + const attachment = driver.findElement({ text: '0x' }); + assert.ok(await attachment, 'attachment'); +} + +async function queueSignatures(driver: Driver) { + await driver.clickElement('#signTypedData'); + await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + await driver.delay(2000); // Delay needed due to a race condition + // To be fixed in https://github.com/MetaMask/metamask-extension/issues/25251 + + await driver.clickElement('#signTypedDataV3'); + await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await driver.delay(2000); + + await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + await driver.clickElement('#signTypedDataV4'); + await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await driver.delay(2000); +} + +async function queueSignaturesAndTransactions(driver: Driver) { + await driver.clickElement('#signTypedData'); + await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await driver.delay(2000); // Delay needed due to a race condition + // To be fixed in https://github.com/MetaMask/metamask-extension/issues/25251 + + await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + await driver.clickElement('#sendButton'); + await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await driver.delay(2000); + + await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + + await driver.clickElement('#signTypedDataV3'); + await driver.waitUntilXWindowHandles(3); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await driver.delay(2000); +} + +async function verifyTransaction( + driver: Driver, + expectedTransactionType: string, +) { + const transactionType = await driver.findElement( + '.confirm-page-container-summary__action__name', + ); + assert.equal(await transactionType.getText(), expectedTransactionType); +} 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 69e4002b7e46..eb815b7727eb 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 @@ -60,11 +60,7 @@ }, "AuthenticationController": { "isSignedIn": "boolean" }, "BridgeController": { - "bridgeState": { - "bridgeFeatureFlags": { - "extensionSupport": "boolean" - } - } + "bridgeState": { "bridgeFeatureFlags": { "extensionSupport": "boolean" } } }, "CronjobController": { "jobs": "object" }, "CurrencyController": { @@ -188,6 +184,7 @@ "useRequestQueue": true, "openSeaEnabled": false, "securityAlertsEnabled": "boolean", + "bitcoinSupportEnabled": "boolean", "addSnapAccountEnabled": "boolean", "advancedGasFee": {}, "featureFlags": {}, @@ -221,9 +218,7 @@ "selectedAddress": "string" }, "PushPlatformNotificationsController": { "fcmToken": "string" }, - "QueuedRequestController": { - "queuedRequestCount": 0 - }, + "QueuedRequestController": { "queuedRequestCount": 0 }, "SelectedNetworkController": { "domains": "object" }, "SignatureController": { "unapprovedMsgs": "object", 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 401f92d138a0..5f9666cb6702 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 @@ -122,6 +122,7 @@ "openSeaEnabled": false, "securityAlertsEnabled": "boolean", "addSnapAccountEnabled": "boolean", + "bitcoinSupportEnabled": "boolean", "advancedGasFee": {}, "incomingTransactionsPreferences": {}, "identities": "object", diff --git a/test/e2e/tests/request-queuing/enable-queuing.spec.js b/test/e2e/tests/request-queuing/enable-queuing.spec.js index 8bff29afa8a6..ccc5f7cec2c7 100644 --- a/test/e2e/tests/request-queuing/enable-queuing.spec.js +++ b/test/e2e/tests/request-queuing/enable-queuing.spec.js @@ -6,7 +6,7 @@ const { const FixtureBuilder = require('../../fixture-builder'); describe('Toggle Request Queuing Setting', function () { - it('should enable the request queuing setting ', async function () { + it('enables the request queuing experimental setting', async function () { await withFixtures( { dapp: true, @@ -38,7 +38,9 @@ describe('Toggle Request Queuing Setting', function () { await driver.clickElement(securityAndPrivacyTabRawLocator); // Toggle request queue setting - await driver.clickElement('.request-queue-toggle'); + await driver.clickElement( + '[data-testid="experimental-setting-toggle-request-queue"]', + ); }, ); }); diff --git a/test/e2e/tests/tokens/nft/import-nft.spec.js b/test/e2e/tests/tokens/nft/import-nft.spec.js index ed3d9056e599..35a34923a26a 100644 --- a/test/e2e/tests/tokens/nft/import-nft.spec.js +++ b/test/e2e/tests/tokens/nft/import-nft.spec.js @@ -107,7 +107,10 @@ describe('Import NFT', function () { await driver.clickElement( '[data-testid="multichain-account-menu-popover-action-button"]', ); - await driver.clickElement({ text: 'Add a new account', tag: 'button' }); + await driver.clickElement({ + text: 'Add a new Ethereum account', + tag: 'button', + }); // By clicking creating button without filling in the account name // the default name would be set as Account 2 diff --git a/test/helpers/setup-after-helper.js b/test/helpers/setup-after-helper.js new file mode 100644 index 000000000000..31bcc6f5d71c --- /dev/null +++ b/test/helpers/setup-after-helper.js @@ -0,0 +1,24 @@ +// This file is for Jest-specific setup only and runs before our Jest tests. + +import nock from 'nock'; +import '@testing-library/jest-dom'; + +jest.mock('webextension-polyfill', () => { + return { + runtime: { + getManifest: () => ({ manifest_version: 2 }), + onMessage: { + removeListener: jest.fn(), + addListener: jest.fn(), + }, + }, + }; +}); + +/* eslint-disable-next-line jest/require-top-level-describe */ +beforeEach(() => { + nock.cleanAll(); +}); + +// Setup window.prompt +global.prompt = () => undefined; diff --git a/test/helpers/setup-helper.js b/test/helpers/setup-helper.js index 98994a8fe037..b694dab84449 100644 --- a/test/helpers/setup-helper.js +++ b/test/helpers/setup-helper.js @@ -22,6 +22,8 @@ global.chrome = { }, }; +global.indexedDB = {}; + nock.disableNetConnect(); nock.enableNetConnect('localhost'); if (typeof beforeEach === 'function') { @@ -125,3 +127,8 @@ if (!window.navigator.clipboard) { if (!window.navigator.clipboard.writeText) { window.navigator.clipboard.writeText = () => undefined; } + +window.SVGPathElement = window.SVGPathElement || { prototype: {} }; + +// scrollIntoView is not available in JSDOM +window.HTMLElement.prototype.scrollIntoView = () => undefined; diff --git a/test/integration/config/env.js b/test/integration/config/env.js new file mode 100644 index 000000000000..62889cf2e317 --- /dev/null +++ b/test/integration/config/env.js @@ -0,0 +1 @@ +require('../../env'); diff --git a/test/integration/config/setup.js b/test/integration/config/setup.js new file mode 100644 index 000000000000..5f67f4e24066 --- /dev/null +++ b/test/integration/config/setup.js @@ -0,0 +1,53 @@ +require('@babel/register'); +require('ts-node').register({ transpileOnly: true }); +const fs = require('node:fs/promises'); +const path = require('path'); + +require('../../helpers/setup-helper'); + +// Fetch +// fetch is part of node js in future versions, thus triggering no-shadow +// eslint-disable-next-line no-shadow +const { default: fetch, Headers, Request, Response } = require('node-fetch'); + +const handleRelativePathRequest = async (url, localeRelativePathRequest) => { + try { + const fullLocalePath = path.join( + process.cwd(), + 'app', + localeRelativePathRequest[0], + ); + + const content = await fs.readFile(fullLocalePath, { encoding: 'utf8' }); + + return new Response(content); + } catch (error) { + throw new Error(`Failed to fetch ${url}: ${error.message}`); + } +}; + +const shimmedFetch = async (url, ...args) => { + try { + return await fetch(url, ...args); + } catch (error) { + if (error.message !== 'Only absolute URLs are supported') { + throw error; + } + + const regex = /_locales\/([^/]+)\/messages\.json/gu; + const localeRelativePathRequest = url.match(regex); + + if (!localeRelativePathRequest?.length) { + throw error; + } + + return handleRelativePathRequest(url, localeRelativePathRequest); + } +}; + +Object.assign(window, { fetch: shimmedFetch, Headers, Request, Response }); +// some of our libraries currently assume that `fetch` is globally available, +// so we need to assign this for tests to run +global.fetch = shimmedFetch; + +global.metamask = {}; diff --git a/test/integration/config/setupAfter.js b/test/integration/config/setupAfter.js new file mode 100644 index 000000000000..39eba1e429a5 --- /dev/null +++ b/test/integration/config/setupAfter.js @@ -0,0 +1,2 @@ +// This file is for Jest-specific setup only and runs before our Jest tests. +import '../../helpers/setup-after-helper'; diff --git a/test/integration/confirmations/signatures/personalSign.test.tsx b/test/integration/confirmations/signatures/personalSign.test.tsx new file mode 100644 index 000000000000..528655924dbe --- /dev/null +++ b/test/integration/confirmations/signatures/personalSign.test.tsx @@ -0,0 +1,200 @@ +import { fireEvent, waitFor } from '@testing-library/react'; +import { ApprovalType } from '@metamask/controller-utils'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { integrationTestRender } from '../../../lib/render-helpers'; +import { shortenAddress } from '../../../../ui/helpers/utils/util'; +import * as backgroundConnection from '../../../../ui/store/background-connection'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, + MetaMetricsEventLocation, +} from '../../../../shared/constants/metametrics'; +import { MESSAGE_TYPE } from '../../../../shared/constants/app'; + +jest.mock('../../../../ui/store/background-connection', () => ({ + ...jest.requireActual('../../../../ui/store/background-connection'), + submitRequestToBackground: jest.fn(), +})); + +const mockedBackgroundConnection = jest.mocked(backgroundConnection); + +const backgroundConnectionMocked = { + onNotification: jest.fn(), +}; + +const getMetaMaskStateWithUnapprovedPersonalSign = (accountAddress: string) => { + const pendingPersonalSignId = '0050d5b0-c023-11ee-a0cb-3390a510a0ab'; + const pendingPersonalSignTime = new Date().getTime(); + return { + ...mockMetaMaskState, + preferences: { + ...mockMetaMaskState.preferences, + redesignedConfirmationsEnabled: true, + }, + unapprovedPersonalMsgs: { + [pendingPersonalSignId]: { + id: pendingPersonalSignId, + status: 'unapproved', + time: pendingPersonalSignTime, + type: MESSAGE_TYPE.PERSONAL_SIGN, + securityProviderResponse: null, + msgParams: { + from: accountAddress, + data: '0x4578616d706c652060706572736f6e616c5f7369676e60206d657373616765', + origin: 'https://metamask.github.io', + siwe: { isSIWEMessage: false, parsedMessage: null }, + signatureMethod: ApprovalType.PersonalSign, + }, + }, + }, + unapprovedPersonalMsgCount: 1, + pendingApprovals: { + [pendingPersonalSignId]: { + id: pendingPersonalSignId, + origin: 'origin', + time: pendingPersonalSignTime, + type: ApprovalType.PersonalSign, + requestData: {}, + requestState: null, + expectsResult: false, + }, + }, + pendingApprovalCount: 1, + }; +}; + +describe('PersonalSign Confirmation', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('displays the header account modal with correct data', async () => { + const account = + mockMetaMaskState.internalAccounts.accounts[ + mockMetaMaskState.internalAccounts + .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts + ]; + + const accountName = account.metadata.name; + const mockedMetaMaskState = getMetaMaskStateWithUnapprovedPersonalSign( + account.address, + ); + + const { getByTestId, queryByTestId } = await integrationTestRender({ + preloadedState: mockedMetaMaskState, + backgroundConnection: backgroundConnectionMocked, + }); + + expect(getByTestId('header-account-name')).toHaveTextContent(accountName); + expect(getByTestId('header-network-display-name')).toHaveTextContent( + 'Chain 5', + ); + + fireEvent.click(getByTestId('header-info__account-details-button')); + + await waitFor(() => { + expect( + getByTestId('confirmation-account-details-modal__account-name'), + ).toBeInTheDocument(); + }); + + expect( + getByTestId('confirmation-account-details-modal__account-name'), + ).toHaveTextContent(accountName); + expect(getByTestId('address-copy-button-text')).toHaveTextContent( + '0x0DCD5...3E7bc', + ); + expect( + getByTestId('confirmation-account-details-modal__account-balance'), + ).toHaveTextContent('1.58271596ETH'); + + let confirmAccountDetailsModalMetricsEvent; + + await waitFor(() => { + confirmAccountDetailsModalMetricsEvent = + mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( + (call) => call[0] === 'trackMetaMetricsEvent', + ); + expect(confirmAccountDetailsModalMetricsEvent?.[0]).toBe( + 'trackMetaMetricsEvent', + ); + }); + + expect(confirmAccountDetailsModalMetricsEvent?.[1]).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + category: MetaMetricsEventCategory.Confirmations, + event: MetaMetricsEventName.AccountDetailsOpened, + properties: { + action: 'Confirm Screen', + location: MetaMetricsEventLocation.SignatureConfirmation, + signature_type: ApprovalType.PersonalSign, + }, + }), + ]), + ); + + fireEvent.click( + getByTestId('confirmation-account-details-modal__close-button'), + ); + + await waitFor(() => { + expect( + queryByTestId('confirmation-account-details-modal__account-name'), + ).not.toBeInTheDocument(); + }); + }); + + it('displays the expected title data', async () => { + const account = + mockMetaMaskState.internalAccounts.accounts[ + mockMetaMaskState.internalAccounts + .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts + ]; + + const mockedMetaMaskState = getMetaMaskStateWithUnapprovedPersonalSign( + account.address, + ); + + const { getByText } = await integrationTestRender({ + preloadedState: mockedMetaMaskState, + backgroundConnection: backgroundConnectionMocked, + }); + + expect(getByText('Signature request')).toBeInTheDocument(); + expect( + getByText( + 'Only confirm this message if you approve the content and trust the requesting site.', + ), + ).toBeInTheDocument(); + }); + + it('displays the MMI header warning when account signing is not the same as the account selected', async () => { + const account = + mockMetaMaskState.internalAccounts.accounts[ + '07c2cfec-36c9-46c4-8115-3836d3ac9047' + ]; + const selectedAccount = + mockMetaMaskState.internalAccounts.accounts[ + mockMetaMaskState.internalAccounts + .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts + ]; + + const mockedMetaMaskState = getMetaMaskStateWithUnapprovedPersonalSign( + account.address, + ); + + const { getByText } = await integrationTestRender({ + preloadedState: mockedMetaMaskState, + backgroundConnection: backgroundConnectionMocked, + }); + + const mismatchAccountText = `Your selected account (${shortenAddress( + selectedAccount.address, + )}) is different than the account trying to sign (${shortenAddress( + account.address, + )})`; + + expect(getByText(mismatchAccountText)).toBeInTheDocument(); + }); +}); diff --git a/test/integration/data/integration-init-state.json b/test/integration/data/integration-init-state.json new file mode 100644 index 000000000000..64f585a65c61 --- /dev/null +++ b/test/integration/data/integration-init-state.json @@ -0,0 +1,2042 @@ +{ + "accounts": { + "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { + "balance": "0x346ba7725f412cbfdb", + "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc" + }, + "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": { + "address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b", + "balance": "0x0" + }, + "0xc42edfcc21ed14dda456aa0756c153f7985d8813": { + "address": "0xc42edfcc21ed14dda456aa0756c153f7985d8813", + "balance": "0x0" + }, + "0xeb9e64b93097bc15f01f13eae97015c57ab64823": { + "address": "0xeb9e64b93097bc15f01f13eae97015c57ab64823", + "balance": "0x0" + }, + "0xca8f1F0245530118D0cf14a06b01Daf8f76Cf281": { + "address": "0xca8f1F0245530118D0cf14a06b01Daf8f76Cf281", + "balance": "0x0" + }, + "0xb552685e3d2790efd64a175b00d51f02cdafee5d": { + "address": "0xb552685e3d2790efd64a175b00d51f02cdafee5d", + "balance": "0x0" + } + }, + "accountsByChainId": { + "0x5": { + "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { + "balance": "0x15f6f0b9d4f8d000" + } + } + }, + "addSnapAccountEnabled": false, + "addressBook": { + "0x5": { + "0xc42edfcc21ed14dda456aa0756c153f7985d8813": { + "address": "0xc42edfcc21ed14dda456aa0756c153f7985d8813", + "chainId": "0x5", + "isEns": false, + "memo": "", + "name": "Address Book Account 1" + } + } + }, + "advancedGasFee": { + "0x5": { + "maxBaseFee": "75", + "priorityFee": "2" + } + }, + "alertEnabledness": { + "unconnectedAccount": true + }, + "allNftContracts": { + "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { + "0x1": [ + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "description": null, + "name": "Munk Generator", + "logo": null, + "symbol": "MUNK", + "assetContractType": "non-fungible", + "createdDate": null, + "schemaName": "ERC721", + "externalLink": null + } + ], + "0x89": [ + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "description": null, + "name": "Munk Generator", + "logo": null, + "symbol": "MUNK", + "assetContractType": "non-fungible", + "createdDate": null, + "schemaName": "ERC721", + "externalLink": null + } + ], + "0xaa36a7": [ + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "description": null, + "name": "Munk Generator", + "logo": null, + "symbol": "MUNK", + "assetContractType": "non-fungible", + "createdDate": null, + "schemaName": "ERC721", + "externalLink": null + } + ], + "0x5": [ + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "description": null, + "name": "Munk Generator", + "logo": null, + "symbol": "MUNK", + "assetContractType": "non-fungible", + "createdDate": null, + "schemaName": "ERC721", + "externalLink": null + }, + { + "address": "0x495f947276749Ce646f68AC8c248420045cb7b5e", + "description": null, + "name": "Punk Generator", + "logo": null, + "symbol": "PUNK", + "assetContractType": "non-fungible", + "createdDate": null, + "schemaName": "ERC1155", + "externalLink": null + } + ], + "0x99": [ + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "description": null, + "name": "Munk Generator", + "logo": null, + "symbol": "MUNK", + "assetContractType": "non-fungible", + "createdDate": null, + "schemaName": "ERC721", + "externalLink": null + } + ] + } + }, + "allNfts": { + "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { + "0x1": [ + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "tokenId": "1", + "name": "MUNK #1", + "description": null, + "image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL", + "standard": "ERC721" + } + ], + "0x89": [ + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "tokenId": "1", + "name": "MUNK #1", + "description": null, + "image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL", + "standard": "ERC721" + } + ], + "0xaa36a7": [ + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "tokenId": "1", + "name": "MUNK #1", + "description": null, + "image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL", + "standard": "ERC721" + } + ], + "0x5": [ + { + "address": "0x495f947276749Ce646f68AC8c248420045cb7b5e", + "tokenId": "58076532811975507823669075598676816378162417803895263482849101575514658701313", + "name": "Punk #4", + "creator": { + "user": { + "username": null + }, + "profile_img_url": null, + "address": "0x806627172af48bd5b0765d3449a7def80d6576ff", + "config": "" + }, + "description": "Red Mohawk bam!", + "image": "https://lh3.googleusercontent.com/BdxvLseXcfl57BiuQcQYdJ64v-aI8din7WPk0Pgo3qQFhAUH-B6i-dCqqc_mCkRIzULmwzwecnohLhrcH8A9mpWIZqA7ygc52Sr81hE", + "standard": "ERC1155" + }, + { + "address": "0x495f947276749Ce646f68AC8c248420045cb7b5e", + "tokenId": "58076532811975507823669075598676816378162417803895263482849101574415147073537", + "name": "Punk #3", + "creator": { + "user": { + "username": null + }, + "profile_img_url": null, + "address": "0x806627172af48bd5b0765d3449a7def80d6576ff", + "config": "" + }, + "description": "Clown PUNK!!!", + "image": "https://lh3.googleusercontent.com/H7VrxaalZv4PF1B8U7ADuc8AfuqTVyzmMEDQ5OXKlx0Tqu5XiwsKYj4j_pAF6wUJjLMQbSN_0n3fuj84lNyRhFW9hyrxqDfY1IiQEQ", + "standard": "ERC1155" + }, + { + "address": "0x495f947276749Ce646f68AC8c248420045cb7b5e", + "tokenId": "58076532811975507823669075598676816378162417803895263482849101573315635445761", + "name": "Punk #2", + "creator": { + "user": { + "username": null + }, + "profile_img_url": null, + "address": "0x806627172af48bd5b0765d3449a7def80d6576ff", + "config": "" + }, + "description": "Got glasses and black hair!", + "image": "https://lh3.googleusercontent.com/CHNTSlKB_Gob-iwTq8jcag6XwBkTqBMLt_vEKeBv18Q4AoPFAEPceqK6mRzkad2s5djx6CT5zbGQwDy81WwtNzViK5dQbG60uAWv", + "standard": "ERC1155" + }, + { + "address": "0x495f947276749Ce646f68AC8c248420045cb7b5e", + "tokenId": "58076532811975507823669075598676816378162417803895263482849101572216123817985", + "name": "Punk #1", + "creator": { + "user": { + "username": null + }, + "profile_img_url": null, + "address": "0x806627172af48bd5b0765d3449a7def80d6576ff", + "config": "" + }, + "image": "https://lh3.googleusercontent.com/4jfPi-nQNWCUXD5qVNVWX7LX2UufU_elEJcvICFlsTdcBXv70asnDEOlI8oKECZxlXq1wseeIXMwmP5tLyOUxMKk", + "standard": "ERC1155" + }, + { + "address": "0x495f947276749Ce646f68AC8c248420045cb7b5e", + "tokenId": "58076532811975507823669075598676816378162417803895263482849101571116612190209", + "name": "Punk #4651", + "creator": { + "user": { + "username": null + }, + "profile_img_url": null, + "address": "0x806627172af48bd5b0765d3449a7def80d6576ff", + "config": "" + }, + "image": "https://lh3.googleusercontent.com/BdxvLseXcfl57BiuQcQYdJ64v-aI8din7WPk0Pgo3qQFhAUH-B6i-dCqqc_mCkRIzULmwzwecnohLhrcH8A9mpWIZqA7ygc52Sr81hE", + "standard": "ERC1155" + }, + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "tokenId": "1", + "name": "MUNK #1", + "description": null, + "image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL", + "standard": "ERC721", + "lastSale": { + "total_price": "4900000000000000", + "event_timestamp": "2023-01-18T21:51:23", + "payment_token": { + "symbol": "ETH" + } + } + }, + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "tokenId": "2", + "name": "MUNK #2", + "description": null, + "image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL", + "standard": "ERC721" + }, + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "tokenId": "3", + "name": "MUNK #3", + "description": null, + "image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL", + "standard": "ERC721" + } + ], + "0x99": [ + { + "address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414", + "tokenId": "1", + "name": "MUNK #1", + "description": null, + "image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL", + "standard": "ERC721" + } + ] + } + }, + "announcements": [], + "approvalFlows": [], + "browserEnvironment": { + "os": "mac", + "browser": "chrome" + }, + "completedOnboarding": true, + "confirmationExchangeRates": {}, + "connectedStatusPopoverHasBeenShown": true, + "contractExchangeRates": { + "0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d": 0.00039345803819379796, + "0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5": 0.00008189274407698049, + "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": 0.0017123, + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": 1.8e-9 + }, + "currencyRates": { + "ETH": { + "conversionRate": 556.12 + } + }, + "currentAppVersion": "11.14.5", + "currentBlockGasLimit": "0x1c9c380", + "currentBlockGasLimitByChainId": { + "0x1": "0x1c9c380" + }, + "currentCurrency": "usd", + "currentLocale": "en", + "customNonceValue": "", + "database": { + "verifiedSnaps": { + "npm:@metamask/test-snap-bip44": { + "id": "npm:@metamask/test-snap-bip44", + "metadata": { + "name": "BIP-44", + "author": { + "name": "Consensys", + "website": "https://consensys.io/" + }, + "website": "https://snaps.consensys.io/", + "summary": "An example Snap that signs messages using BLS.", + "description": "An example Snap that signs messages using BLS.", + "audits": [ + { + "auditor": "Consensys Diligence", + "report": "https://consensys.io/diligence/audits/" + } + ], + "category": "interoperability", + "support": { + "contact": "https://github.com/MetaMask" + }, + "sourceCode": "https://github.com/MetaMask/test-snaps" + }, + "versions": { + "5.1.2": { + "checksum": "L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=" + }, + "5.1.3": { + "checksum": "21k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=" + }, + "6.0.0": { + "checksum": "31k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=" + } + } + } + } + }, + "defaultHomeActiveTabName": "activity", + "desktopEnabled": false, + "detectedTokens": [], + "disabledRpcMethodPreferences": { + "eth_sign": false + }, + "dismissSeedBackUpReminder": false, + "ensEntries": {}, + "ensResolutionsByAddress": {}, + "featureFlags": {}, + "firstTimeFlowType": "create", + "forgottenPassword": false, + "fragments": {}, + "fullScreenGasPollTokens": [], + "gasEstimateType": "fee-market", + "gasFeeEstimates": { + "low": { + "minWaitTimeEstimate": 180000, + "maxWaitTimeEstimate": 300000, + "suggestedMaxPriorityFeePerGas": "3", + "suggestedMaxFeePerGas": "53" + }, + "medium": { + "minWaitTimeEstimate": 15000, + "maxWaitTimeEstimate": 60000, + "suggestedMaxPriorityFeePerGas": "7", + "suggestedMaxFeePerGas": "70" + }, + "high": { + "minWaitTimeEstimate": 0, + "maxWaitTimeEstimate": 15000, + "suggestedMaxPriorityFeePerGas": "10", + "suggestedMaxFeePerGas": "100" + }, + "estimatedBaseFee": "50", + "historicalBaseFeeRange": ["28.533098435", "70.351148354"], + "baseFeeTrend": "up", + "latestPriorityFeeRange": ["1", "40"], + "historicalPriorityFeeRange": ["0.1458417", "700.156384646"], + "priorityFeeTrend": "down", + "networkCongestion": 0.90625 + }, + "gasFeeEstimatesByChainId": { + "0x5": { + "estimatedGasFeeTimeBounds": { + "lowerTimeBound": 15000, + "upperTimeBound": 60000 + }, + "gasEstimateType": "fee-market", + "gasFeeEstimates": { + "low": { + "minWaitTimeEstimate": 180000, + "maxWaitTimeEstimate": 300000, + "suggestedMaxPriorityFeePerGas": "3", + "suggestedMaxFeePerGas": "53" + }, + "medium": { + "minWaitTimeEstimate": 15000, + "maxWaitTimeEstimate": 60000, + "suggestedMaxPriorityFeePerGas": "7", + "suggestedMaxFeePerGas": "70" + }, + "high": { + "minWaitTimeEstimate": 0, + "maxWaitTimeEstimate": 15000, + "suggestedMaxPriorityFeePerGas": "10", + "suggestedMaxFeePerGas": "100" + }, + "estimatedBaseFee": "50", + "historicalBaseFeeRange": ["28.533098435", "70.351148354"], + "baseFeeTrend": "up", + "latestPriorityFeeRange": ["1", "40"], + "historicalPriorityFeeRange": ["0.1458417", "700.156384646"], + "priorityFeeTrend": "down", + "networkCongestion": 0.90625 + } + } + }, + "hadAdvancedGasFeesSetPriorToMigration92_3": false, + "hasDismissedOpenSeaToBlockaidBanner": false, + "hiddenAccountList": [], + "identities": { + "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { + "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc", + "name": "Test Account" + }, + "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": { + "address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b", + "name": "Test Account 2" + }, + "0xc42edfcc21ed14dda456aa0756c153f7985d8813": { + "address": "0xc42edfcc21ed14dda456aa0756c153f7985d8813", + "name": "Test Ledger 1" + }, + "0xeb9e64b93097bc15f01f13eae97015c57ab64823": { + "name": "Test Account 3", + "address": "0xeb9e64b93097bc15f01f13eae97015c57ab64823" + }, + "0xca8f1F0245530118D0cf14a06b01Daf8f76Cf281": { + "name": "Custody test", + "address": "0xca8f1F0245530118D0cf14a06b01Daf8f76Cf281" + } + }, + "ignoredNfts": [], + "ignoredTokens": [], + "incomingTransactionsPreferences": { + "0x1": true, + "0xe708": false, + "0xfa": true, + "0x5": false, + "0xaa36a7": true, + "0xe704": true + }, + "internalAccounts": { + "accounts": { + "cf8dace4-9439-4bd4-b3a8-88c821c8fcb3": { + "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc", + "id": "cf8dace4-9439-4bd4-b3a8-88c821c8fcb3", + "metadata": { + "name": "Test Account", + "keyring": { + "type": "HD Key Tree" + } + }, + "options": {}, + "methods": [ + "personal_sign", + "eth_sign", + "eth_signTransaction", + "eth_signTypedData_v1", + "eth_signTypedData_v3", + "eth_signTypedData_v4" + ], + "type": "eip155:eoa" + }, + "07c2cfec-36c9-46c4-8115-3836d3ac9047": { + "address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b", + "id": "07c2cfec-36c9-46c4-8115-3836d3ac9047", + "metadata": { + "name": "Test Account 2", + "keyring": { + "type": "HD Key Tree" + } + }, + "options": {}, + "methods": [ + "personal_sign", + "eth_sign", + "eth_signTransaction", + "eth_signTypedData_v1", + "eth_signTypedData_v3", + "eth_signTypedData_v4" + ], + "type": "eip155:eoa" + }, + "15e69915-2a1a-4019-93b3-916e11fd432f": { + "address": "0xc42edfcc21ed14dda456aa0756c153f7985d8813", + "id": "15e69915-2a1a-4019-93b3-916e11fd432f", + "metadata": { + "name": "Ledger Hardware 2", + "keyring": { + "type": "Ledger Hardware" + } + }, + "options": {}, + "methods": [ + "personal_sign", + "eth_sign", + "eth_signTransaction", + "eth_signTypedData_v1", + "eth_signTypedData_v3", + "eth_signTypedData_v4" + ], + "type": "eip155:eoa" + }, + "784225f4-d30b-4e77-a900-c8bbce735b88": { + "address": "0xeb9e64b93097bc15f01f13eae97015c57ab64823", + "id": "784225f4-d30b-4e77-a900-c8bbce735b88", + "metadata": { + "name": "Test Account 3", + "keyring": { + "type": "HD Key Tree" + } + }, + "options": {}, + "methods": [ + "personal_sign", + "eth_sign", + "eth_signTransaction", + "eth_signTypedData_v1", + "eth_signTypedData_v3", + "eth_signTypedData_v4" + ], + "type": "eip155:eoa" + }, + "694225f4-d30b-4e77-a900-c8bbce735b42": { + "address": "0xca8f1F0245530118D0cf14a06b01Daf8f76Cf281", + "id": "694225f4-d30b-4e77-a900-c8bbce735b42", + "metadata": { + "name": "Test Account 4", + "keyring": { + "type": "Custody test" + } + }, + "options": {}, + "methods": [ + "personal_sign", + "eth_sign", + "eth_signTransaction", + "eth_signTypedData_v1", + "eth_signTypedData_v3", + "eth_signTypedData_v4" + ], + "type": "eip155:eoa" + }, + "c3deeb99-ba0d-4a4e-a0aa-033fc1f79ae3": { + "address": "0xb552685e3d2790efd64a175b00d51f02cdafee5d", + "id": "c3deeb99-ba0d-4a4e-a0aa-033fc1f79ae3", + "metadata": { + "name": "Snap Account 1", + "keyring": { + "type": "Snap Keyring" + }, + "snap": { + "id": "snap-id", + "name": "snap-name" + } + }, + "options": {}, + "methods": [ + "personal_sign", + "eth_sign", + "eth_signTransaction", + "eth_signTypedData_v1", + "eth_signTypedData_v3", + "eth_signTypedData_v4" + ], + "type": "eip155:eoa" + } + }, + "selectedAccount": "cf8dace4-9439-4bd4-b3a8-88c821c8fcb3" + }, + "ipfsGateway": "dweb.link", + "isAccountMenuOpen": false, + "isInitialized": true, + "isIpfsGatewayEnabled": true, + "isNetworkMenuOpen": false, + "isUnlocked": true, + "jobs": {}, + "keyrings": [ + { + "type": "HD Key Tree", + "accounts": [ + "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc", + "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b" + ] + }, + { + "type": "Ledger Hardware", + "accounts": ["0xc42edfcc21ed14dda456aa0756c153f7985d8813"] + }, + { + "type": "Simple Key Pair", + "accounts": ["0xeb9e64b93097bc15f01f13eae97015c57ab64823"] + }, + { + "type": "Snap Keyring", + "accounts": ["0xb552685e3d2790efd64a175b00d51f02cdafee5d"] + }, + { + "type": "Custody test", + "accounts": ["0xca8f1F0245530118D0cf14a06b01Daf8f76Cf281"] + } + ], + "nameSources": { + "ens": { + "label": "Ethereum Name Service (ENS)" + }, + "etherscan": { + "label": "Etherscan (Verified Contract Name)" + }, + "lens": { + "label": "Lens Protocol" + }, + "token": { + "label": "Blockchain (Token Name)" + } + }, + "names": { + "ethereumAddress": { + "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { + "*": { + "name": "Test Account", + "origin": "account-identity", + "proposedNames": {}, + "sourceId": null + } + }, + "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": { + "*": { + "name": "Test Account 2", + "origin": "account-identity", + "proposedNames": {}, + "sourceId": null + } + } + } + }, + "networkConfigurations": { + "testNetworkConfigurationId": { + "rpcUrl": "https://testrpc.com", + "chainId": "0x1", + "nickname": "Custom Mainnet RPC", + "type": "rpc", + "id": "testNetworkConfigurationId" + }, + "chain5": { + "type": "rpc", + "chainId": "0x5", + "ticker": "ETH", + "nickname": "Chain 5", + "id": "chain5" + } + }, + "networksMetadata": { + "goerli": { + "EIPS": { + "1559": true + }, + "status": "available" + } + }, + "nftsDropdownState": { + "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { + "0x5": { + "0x495f947276749Ce646f68AC8c248420045cb7b5e": false + } + } + }, + "notifications": { + "test": { + "id": "test", + "origin": "local:http://localhost:8086/", + "createdDate": 1652967897732, + "readDate": null, + "message": "Hello, http://localhost:8086!" + }, + "test2": { + "id": "test2", + "origin": "local:http://localhost:8086/", + "createdDate": 1652967897732, + "readDate": 1652967897732, + "message": "Hello, http://localhost:8086!" + } + }, + "openSeaEnabled": false, + "orderedNetworkList": [], + "participateInMetaMetrics": false, + "pendingApprovalCount": 1, + "pendingApprovals": { + "testApprovalId": { + "id": "testApprovalId", + "time": 1528133319641, + "origin": "metamask", + "type": "transaction", + "requestData": { + "txId": "testTransactionId" + }, + "requestState": { + "test": "value" + } + } + }, + "permissionHistory": { + "https://metamask.github.io": { + "eth_accounts": { + "accounts": { + "0dcd5d886577d5081b0c52e242ef29e70be3e7bc": 1709225290848 + } + } + } + }, + "pinnedAccountList": [], + "preferences": { + "hideZeroBalanceTokens": false, + "showExtensionInFullSizeView": false, + "showFiatInTestnets": false, + "showTestNetworks": true, + "smartTransactionsOptInStatus": false, + "useNativeCurrencyAsPrimaryCurrency": true, + "petnamesEnabled": false + }, + "preventPollingOnNetworkRestart": true, + "previousAppVersion": "11.14.4", + "previousMigrationVersion": 112, + "providerConfig": { + "type": "rpc", + "nickname": "goerli", + "chainId": "0x5", + "ticker": "ETH", + "id": "chain5" + }, + "securityAlertsEnabled": true, + "seedPhraseBackedUp": true, + "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc", + "selectedNetworkClientId": "goerli", + "showAccountBanner": false, + "showBetaHeader": false, + "showNetworkBanner": true, + "showPermissionsTour": true, + "showProductTour": false, + "showTestnetMessageInDropdown": true, + "signatureSecurityAlertResponses": {}, + "smartTransactionsState": { + "fees": {}, + "liveness": true, + "smartTransactions": { + "0x1": [], + "0x5": [] + }, + "userOptInV2": true + }, + "snapRegistryList": { + "a51ea3a8-f1b0-4613-9440-b80e2236713b": { + "id": "a51ea3a8-f1b0-4613-9440-b80e2236713b", + "snapId": "npm:@metamask/snap-simple-keyring", + "iconUrl": "", + "snapTitle": "Metamask Simple Keyring", + "snapSlug": "Secure your account with MetaMask Mobile", + "snapDescription": "A simple private key is a randomly generated string of characters that is used to sign transactions. This private key is stored securely within this snap.", + "tags": ["EOA"], + "developer": "Metamask", + "website": "https://www.consensys.io/", + "auditUrls": ["auditUrl1", "auditUrl2"], + "version": "1.0.0", + "lastUpdated": "April 20, 2023" + } + }, + "snapStates": {}, + "snaps": { + "npm:@metamask/test-snap-bip44": { + "id": "npm:@metamask/test-snap-bip44", + "origin": "npm:@metamask/test-snap-bip44", + "version": "5.1.2", + "iconUrl": null, + "initialPermissions": { + "endowment:ethereum-provider": {} + }, + "manifest": { + "description": "An example Snap that signs messages using BLS.", + "proposedName": "BIP-44 Test Snap", + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/test-snaps.git" + }, + "source": { + "location": { + "npm": { + "filePath": "dist/bundle.js", + "packageName": "@metamask/test-snap-bip44", + "registry": "https://registry.npmjs.org" + } + }, + "shasum": "L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=" + }, + "version": "5.1.2" + }, + "versionHistory": [ + { + "date": 1680686075921, + "origin": "https://metamask.github.io", + "version": "5.1.2" + } + ] + }, + "npm:@metamask/test-snap-bip32": { + "id": "npm:@metamask/test-snap-bip32", + "origin": "npm:@metamask/test-snap-bip32", + "version": "5.1.2", + "iconUrl": null, + "initialPermissions": { + "endowment:ethereum-provider": {} + }, + "manifest": { + "description": "An example Snap that signs messages using BLS.", + "proposedName": "BIP-32 Test Snap", + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/test-snaps.git" + }, + "source": { + "location": { + "npm": { + "filePath": "dist/bundle.js", + "packageName": "@metamask/test-snap-bip32", + "registry": "https://registry.npmjs.org" + } + }, + "shasum": "L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=" + }, + "version": "5.1.2" + }, + "versionHistory": [ + { + "date": 1680686075921, + "origin": "https://metamask.github.io", + "version": "5.1.2" + } + ] + }, + "npm:@metamask/test-snap-getEntropy": { + "id": "npm:@metamask/test-snap-getEntropy", + "origin": "npm:@metamask/test-snap-getEntropy", + "version": "5.1.2", + "iconUrl": null, + "initialPermissions": { + "endowment:ethereum-provider": {} + }, + "manifest": { + "description": "An example Snap that can derive snap specific entropy.", + "proposedName": "Get Entropy Test Snap", + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/test-snaps.git" + }, + "source": { + "location": { + "npm": { + "filePath": "dist/bundle.js", + "packageName": "@metamask/test-snap-getEntropy", + "registry": "https://registry.npmjs.org" + } + }, + "shasum": "L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=" + }, + "version": "5.1.2" + }, + "versionHistory": [ + { + "date": 1680686075921, + "origin": "https://metamask.github.io", + "version": "5.1.2" + } + ] + }, + "npm:@metamask/test-snap-networkAccess": { + "id": "npm:@metamask/test-snap-networkAccess", + "origin": "npm:@metamask/test-snap-networkAccess", + "version": "5.1.2", + "iconUrl": null, + "initialPermissions": { + "endowment:ethereum-provider": {} + }, + "manifest": { + "description": "An example Snap that has network access.", + "proposedName": "Network Access Test Snap", + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/test-snaps.git" + }, + "source": { + "location": { + "npm": { + "filePath": "dist/bundle.js", + "packageName": "@metamask/test-snap-networkAccess", + "registry": "https://registry.npmjs.org" + } + }, + "shasum": "L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=" + }, + "version": "5.1.2" + }, + "versionHistory": [ + { + "date": 1680686075921, + "origin": "https://metamask.github.io", + "version": "5.1.2" + } + ] + }, + "npm:@metamask/test-snap-wasm": { + "id": "npm:@metamask/test-snap-wasm", + "origin": "npm:@metamask/test-snap-wasm", + "version": "5.1.2", + "iconUrl": null, + "initialPermissions": { + "endowment:ethereum-provider": {} + }, + "manifest": { + "description": "An example Snap that has WASM access.", + "proposedName": "WASM Test Snap", + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/test-snaps.git" + }, + "source": { + "location": { + "npm": { + "filePath": "dist/bundle.js", + "packageName": "@metamask/test-snap-wasm", + "registry": "https://registry.npmjs.org" + } + }, + "shasum": "L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=" + }, + "version": "5.1.2" + }, + "versionHistory": [ + { + "date": 1680686075921, + "origin": "https://metamask.github.io", + "version": "5.1.2" + } + ] + }, + "npm:@metamask/test-snap-notify": { + "id": "npm:@metamask/test-snap-notify", + "origin": "npm:@metamask/test-snap-notify", + "version": "5.1.2", + "iconUrl": null, + "initialPermissions": { + "endowment:ethereum-provider": {} + }, + "manifest": { + "description": "An example Snap that can send notifications.", + "proposedName": "Notification Test Snap", + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/test-snaps.git" + }, + "source": { + "location": { + "npm": { + "filePath": "dist/bundle.js", + "packageName": "@metamask/test-snap-notify", + "registry": "https://registry.npmjs.org" + } + }, + "shasum": "L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=" + }, + "version": "5.1.2" + }, + "versionHistory": [ + { + "date": 1680686075921, + "origin": "https://metamask.github.io", + "version": "5.1.2" + } + ] + }, + "npm:@metamask/test-snap-dialog": { + "id": "npm:@metamask/test-snap-dialog", + "origin": "npm:@metamask/test-snap-dialog", + "version": "5.1.2", + "iconUrl": null, + "initialPermissions": { + "endowment:ethereum-provider": {} + }, + "manifest": { + "description": "An example Snap that can send dialog prompts.", + "proposedName": "Dialog Test Snap", + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/test-snaps.git" + }, + "source": { + "location": { + "npm": { + "filePath": "dist/bundle.js", + "packageName": "@metamask/test-snap-dialog", + "registry": "https://registry.npmjs.org" + } + }, + "shasum": "L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=" + }, + "version": "5.1.2" + }, + "versionHistory": [ + { + "date": 1680686075921, + "origin": "https://metamask.github.io", + "version": "5.1.2" + } + ] + } + }, + "snapsAddSnapAccountModalDismissed": false, + "storageMetadata": [], + "subjectMetadata": { + "npm:@metamask/test-snap-bip44": { + "name": "@metamask/test-snap-bip44", + "version": "1.2.3", + "subjectType": "snap" + } + }, + "swapsState": { + "quotes": {}, + "quotesPollingLimitEnabled": false, + "fetchParams": null, + "tokens": null, + "tradeTxId": null, + "approveTxId": null, + "quotesLastFetched": null, + "customMaxGas": "", + "customGasPrice": null, + "customMaxFeePerGas": null, + "customMaxPriorityFeePerGas": null, + "swapsUserFeeLevel": "", + "selectedAggId": null, + "customApproveTxData": "", + "errorKey": "", + "topAggId": null, + "routeState": "", + "swapsFeatureIsLive": true, + "saveFetchedQuotes": false, + "swapsQuoteRefreshTime": 60000, + "swapsQuotePrefetchingRefreshTime": 60000, + "swapsStxBatchStatusRefreshTime": 10000, + "swapsStxGetTransactionsRefreshTime": 10000, + "swapsStxMaxFeeMultiplier": 2, + "swapsFeatureFlags": { + "arbitrum": { + "extensionActive": true, + "extension_active": true, + "fallbackToV1": false, + "fallback_to_v1": false, + "mobileActive": true, + "mobileActiveAndroid": true, + "mobileActiveIOS": true, + "mobile_active": true, + "smartTransactions": {} + }, + "avalanche": { + "extensionActive": true, + "extension_active": true, + "fallbackToV1": false, + "fallback_to_v1": false, + "mobileActive": true, + "mobileActiveAndroid": true, + "mobileActiveIOS": true, + "mobile_active": true, + "smartTransactions": {} + }, + "base": { + "extensionActive": false, + "extension_active": false, + "fallbackToV1": false, + "fallback_to_v1": false, + "mobileActive": false, + "mobileActiveAndroid": false, + "mobileActiveIOS": false, + "mobile_active": false, + "smartTransactions": {} + }, + "bsc": { + "extensionActive": true, + "extension_active": true, + "fallbackToV1": false, + "fallback_to_v1": false, + "mobileActive": true, + "mobileActiveAndroid": true, + "mobileActiveIOS": true, + "mobile_active": true, + "smartTransactions": {} + }, + "ethereum": { + "extensionActive": true, + "extension_active": true, + "fallbackToV1": false, + "fallback_to_v1": false, + "mobileActive": true, + "mobileActiveAndroid": true, + "mobileActiveIOS": true, + "mobile_active": true, + "smartTransactions": { + "expectedDeadline": 45, + "maxDeadline": 150, + "returnTxHashAsap": false + } + }, + "linea": { + "extensionActive": true, + "extension_active": true, + "fallbackToV1": false, + "fallback_to_v1": false, + "mobileActive": true, + "mobileActiveAndroid": true, + "mobileActiveIOS": true, + "mobile_active": true, + "smartTransactions": {} + }, + "optimism": { + "extensionActive": true, + "extension_active": true, + "fallbackToV1": false, + "fallback_to_v1": false, + "mobileActive": true, + "mobileActiveAndroid": true, + "mobileActiveIOS": true, + "mobile_active": true, + "smartTransactions": {} + }, + "polygon": { + "extensionActive": true, + "extension_active": true, + "fallbackToV1": false, + "fallback_to_v1": false, + "mobileActive": true, + "mobileActiveAndroid": true, + "mobileActiveIOS": true, + "mobile_active": true, + "smartTransactions": {} + }, + "smartTransactions": { + "extensionActive": true, + "mobileActive": true, + "mobileActiveAndroid": false, + "mobileActiveIOS": false + }, + "smart_transactions": { + "extension_active": true, + "mobile_active": true + }, + "swapRedesign": { + "extensionActive": true, + "mobileActive": false + }, + "zksync": { + "extensionActive": true, + "extension_active": true, + "fallbackToV1": false, + "fallback_to_v1": false, + "mobileActive": true, + "mobileActiveAndroid": true, + "mobileActiveIOS": true, + "mobile_active": true, + "smartTransactions": {} + } + } + }, + "switchedNetworkNeverShowMessage": false, + "termsOfUseLastAgreed": 1692109813199, + "theme": "os", + "tokenList": { + "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": { + "address": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", + "symbol": "WBTC", + "decimals": 8, + "name": "Wrapped Bitcoin", + "iconUrl": "https://s3.amazonaws.com/airswap-token-images/WBTC.png", + "aggregators": [ + "airswapLight", + "bancor", + "cmc", + "coinGecko", + "kleros", + "oneInch", + "paraswap", + "pmm", + "totle", + "zapper", + "zerion", + "zeroEx" + ], + "occurrences": 12 + }, + "0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e": { + "address": "0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e", + "symbol": "YFI", + "decimals": 18, + "name": "yearn.finance", + "iconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e/logo.png", + "aggregators": [ + "airswapLight", + "bancor", + "cmc", + "coinGecko", + "kleros", + "oneInch", + "paraswap", + "pmm", + "totle", + "zapper", + "zerion", + "zeroEx" + ], + "occurrences": 12 + } + }, + "tokens": [ + { + "address": "0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d", + "symbol": "TEST", + "decimals": "0" + }, + { + "address": "0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5", + "decimals": "8", + "symbol": "TEST2" + }, + { + "address": "0x617b3f8050a0bd94b6b1da02b4384ee5b4df13f4", + "symbol": "META", + "decimals": "18" + } + ], + "transactions": [ + { + "id": 3387511061307736, + "time": 1528133130531, + "status": "confirmed", + "chainId": "0x5", + "loadingDefaults": false, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0x92e659448c48fc926ec942d0da1459260d36bb33", + "value": "0x1bc16d674ec80000", + "gas": "0xcf08", + "gasPrice": "0x3b9aca00", + "nonce": "0xb5" + }, + "history": [ + { + "id": 3387511061307736, + "time": 1528133130531, + "status": "unapproved", + "chainId": "0x5", + "loadingDefaults": true, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0x92e659448c48fc926ec942d0da1459260d36bb33", + "value": "0x1bc16d674ec80000", + "gas": "0xcf08", + "gasPrice": "0x3b9aca00" + } + }, + [ + { + "op": "replace", + "path": "/loadingDefaults", + "value": false, + "timestamp": 1528133130666 + } + ], + [ + { + "op": "add", + "path": "/origin", + "value": "MetaMask", + "note": "#newUnapprovedTransaction - adding the origin", + "timestamp": 1528133130667 + } + ], + [], + [ + { + "op": "replace", + "path": "/status", + "value": "approved", + "note": "txStateManager: setting status to approved", + "timestamp": 1528133131716 + } + ], + [ + { + "op": "add", + "path": "/txParams/nonce", + "value": "0xb5", + "note": "transactions#approveTransaction", + "timestamp": 1528133131806 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "signed", + "note": "transactions#publishTransaction", + "timestamp": 1528133131825 + }, + { + "op": "add", + "path": "/rawTx", + "value": "0xf86c81b5843b9aca0082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba03f879cd33a31180da38545d0f809822e00ddf35954d8b0ece83bacf22347ce54a06ad050487978e425ca6a014ed55ea8e9a190069863ed96a0eefa88d729ea1eda" + } + ], + [], + [ + { + "op": "add", + "path": "/hash", + "value": "0x516b77569173a04c76fdb6545cf279ebd0c75f5d25d6e4ce019925205f0e3709", + "note": "transactions#setTxHash", + "timestamp": 1528133131951 + } + ], + [ + { + "op": "add", + "path": "/submittedTime", + "value": 1528133131951, + "note": "txStateManager - add submitted time stamp", + "timestamp": 1528133131952 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "submitted", + "note": "txStateManager: setting status to submitted", + "timestamp": 1528133131955 + } + ], + [ + { + "op": "add", + "path": "/firstRetryBlockNumber", + "value": "0x24af6b", + "note": "transactions/pending-tx-tracker#event: tx:block-update", + "timestamp": 1528133134414 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "confirmed", + "note": "txStateManager: setting status to confirmed", + "timestamp": 1528133158516 + } + ] + ], + "origin": "MetaMask", + "rawTx": "0xf86c81b5843b9aca0082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba03f879cd33a31180da38545d0f809822e00ddf35954d8b0ece83bacf22347ce54a06ad050487978e425ca6a014ed55ea8e9a190069863ed96a0eefa88d729ea1eda", + "hash": "0x516b77569173a04c76fdb6545cf279ebd0c75f5d25d6e4ce019925205f0e3709", + "submittedTime": 1528133131951, + "firstRetryBlockNumber": "0x24af6b" + }, + { + "id": 3387511061307737, + "time": 1528133149983, + "status": "confirmed", + "chainId": "0x5", + "loadingDefaults": false, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0x92e659448c48fc926ec942d0da1459260d36bb33", + "value": "0x1bc16d674ec80000", + "gas": "0xcf08", + "gasPrice": "0x3b9aca00", + "nonce": "0xb6" + }, + "history": [ + { + "id": 3387511061307737, + "time": 1528133149983, + "status": "unapproved", + "chainId": "0x5", + "loadingDefaults": true, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0x92e659448c48fc926ec942d0da1459260d36bb33", + "value": "0x1bc16d674ec80000", + "gas": "0xcf08", + "gasPrice": "0x3b9aca00" + } + }, + [ + { + "op": "replace", + "path": "/loadingDefaults", + "value": false, + "timestamp": 1528133150011 + } + ], + [ + { + "op": "add", + "path": "/origin", + "value": "MetaMask", + "note": "#newUnapprovedTransaction - adding the origin", + "timestamp": 1528133150013 + } + ], + [], + [ + { + "op": "replace", + "path": "/status", + "value": "approved", + "note": "txStateManager: setting status to approved", + "timestamp": 1528133151102 + } + ], + [ + { + "op": "add", + "path": "/txParams/nonce", + "value": "0xb6", + "note": "transactions#approveTransaction", + "timestamp": 1528133151189 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "signed", + "note": "transactions#publishTransaction", + "timestamp": 1528133151203 + }, + { + "op": "add", + "path": "/rawTx", + "value": "0xf86c81b6843b9aca0082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba0692deaabf0d79544d41e7c475ad43760679a4f25d0fee908b1da308db1a291a7a0384db85fc6c843ea25986a0760f3c50ab6504fc559fc71fc7f23f60950eb316d" + } + ], + [], + [ + { + "op": "add", + "path": "/hash", + "value": "0x9271b266d05022cfa841362fae43763ebafcee540d84278b0157ef4a68d4e26f", + "note": "transactions#setTxHash", + "timestamp": 1528133151342 + } + ], + [ + { + "op": "add", + "path": "/submittedTime", + "value": 1528133151347, + "note": "txStateManager - add submitted time stamp", + "timestamp": 1528133151347 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "submitted", + "note": "txStateManager: setting status to submitted", + "timestamp": 1528133151368 + } + ], + [ + { + "op": "add", + "path": "/firstRetryBlockNumber", + "value": "0x24af6d", + "note": "transactions/pending-tx-tracker#event: tx:block-update", + "timestamp": 1528133158532 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "confirmed", + "note": "txStateManager: setting status to confirmed", + "timestamp": 1528133190636 + } + ] + ], + "origin": "MetaMask", + "rawTx": "0xf86c81b6843b9aca0082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba0692deaabf0d79544d41e7c475ad43760679a4f25d0fee908b1da308db1a291a7a0384db85fc6c843ea25986a0760f3c50ab6504fc559fc71fc7f23f60950eb316d", + "hash": "0x9271b266d05022cfa841362fae43763ebafcee540d84278b0157ef4a68d4e26f", + "submittedTime": 1528133151347, + "firstRetryBlockNumber": "0x24af6d" + }, + { + "id": 3387511061307738, + "time": 1528133180635, + "status": "confirmed", + "chainId": "0x5", + "loadingDefaults": false, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0x92e659448c48fc926ec942d0da1459260d36bb33", + "value": "0x1bc16d674ec80000", + "gas": "0xcf08", + "gasPrice": "0x12a05f200", + "nonce": "0xb7" + }, + "history": [ + { + "id": 3387511061307738, + "time": 1528133180635, + "status": "unapproved", + "chainId": "0x5", + "loadingDefaults": true, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0x92e659448c48fc926ec942d0da1459260d36bb33", + "value": "0x1bc16d674ec80000", + "gas": "0xcf08", + "gasPrice": "0x12a05f200" + } + }, + [ + { + "op": "replace", + "path": "/loadingDefaults", + "value": false, + "timestamp": 1528133180720 + } + ], + [ + { + "op": "add", + "path": "/origin", + "value": "MetaMask", + "note": "#newUnapprovedTransaction - adding the origin", + "timestamp": 1528133180722 + } + ], + [], + [ + { + "op": "replace", + "path": "/status", + "value": "approved", + "note": "txStateManager: setting status to approved", + "timestamp": 1528133181623 + } + ], + [ + { + "op": "add", + "path": "/txParams/nonce", + "value": "0xb7", + "note": "transactions#approveTransaction", + "timestamp": 1528133181726 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "signed", + "note": "transactions#publishTransaction", + "timestamp": 1528133181749 + }, + { + "op": "add", + "path": "/rawTx", + "value": "0xf86d81b785012a05f20082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba086f9846798be6988c39a5cf85f0dbe267e59ca0b96a6a7077e92cba33e10a258a064ffa52ac90c238ce21e6f085283216191b185a1eccd7daae6e2ab66ba26ada0" + } + ], + [], + [ + { + "op": "add", + "path": "/hash", + "value": "0x4e061e977c099735bc9e5203e717f7d9dccb3fcb2f82031a12a3ed326f95d43b", + "note": "transactions#setTxHash", + "timestamp": 1528133181885 + } + ], + [ + { + "op": "add", + "path": "/submittedTime", + "value": 1528133181885, + "note": "txStateManager - add submitted time stamp", + "timestamp": 1528133181885 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "submitted", + "note": "txStateManager: setting status to submitted", + "timestamp": 1528133181888 + } + ], + [ + { + "op": "add", + "path": "/firstRetryBlockNumber", + "value": "0x24af6f", + "note": "transactions/pending-tx-tracker#event: tx:block-update", + "timestamp": 1528133190653 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "confirmed", + "note": "txStateManager: setting status to confirmed", + "timestamp": 1528133222745 + } + ] + ], + "origin": "MetaMask", + "rawTx": "0xf86d81b785012a05f20082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba086f9846798be6988c39a5cf85f0dbe267e59ca0b96a6a7077e92cba33e10a258a064ffa52ac90c238ce21e6f085283216191b185a1eccd7daae6e2ab66ba26ada0", + "hash": "0x4e061e977c099735bc9e5203e717f7d9dccb3fcb2f82031a12a3ed326f95d43b", + "submittedTime": 1528133181885, + "firstRetryBlockNumber": "0x24af6f" + }, + { + "id": 3387511061307739, + "time": 1528133223918, + "status": "confirmed", + "chainId": "0x5", + "loadingDefaults": false, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0xfe2149773b3513703e79ad23d05a778a185016ee", + "value": "0xaa87bee538000", + "data": "0xea94496b000000000000000000000000000000000000000000000000000000000001e1eb000000000000000000000000000000000000000000000000000000000001de33", + "gasPrice": "0x3b9aca00", + "gas": "0x6169e", + "nonce": "0xb8" + }, + "history": [ + { + "id": 3387511061307739, + "time": 1528133223918, + "status": "unapproved", + "chainId": "0x5", + "loadingDefaults": true, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0xfe2149773b3513703e79ad23d05a778a185016ee", + "value": "0xaa87bee538000", + "data": "0xea94496b000000000000000000000000000000000000000000000000000000000001e1eb000000000000000000000000000000000000000000000000000000000001de33", + "gasPrice": "0x3b9aca00" + } + }, + [ + { + "op": "add", + "path": "/txParams/gas", + "value": "0x6169e", + "timestamp": 1528133225488 + }, + { + "op": "replace", + "path": "/loadingDefaults", + "value": false + } + ], + [ + { + "note": "#newUnapprovedTransaction - adding the origin", + "op": "add", + "path": "/origin", + "value": "crypko.ai" + } + ], + [], + [ + { + "op": "replace", + "path": "/status", + "value": "approved", + "note": "txStateManager: setting status to approved", + "timestamp": 1528133227279 + } + ], + [ + { + "op": "add", + "path": "/txParams/nonce", + "value": "0xb8", + "note": "transactions#approveTransaction", + "timestamp": 1528133227374 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "signed", + "note": "transactions#publishTransaction", + "timestamp": 1528133227405 + }, + { + "op": "add", + "path": "/rawTx", + "value": "0xf8b181b8843b9aca008306169e94fe2149773b3513703e79ad23d05a778a185016ee870aa87bee538000b844ea94496b000000000000000000000000000000000000000000000000000000000001e1eb000000000000000000000000000000000000000000000000000000000001de332ca07bb2efbb8529d67606f9f89e7934c594a31d50c7d24a3286c20a2944a3b8c2a9a07b55ebd8aa28728ce0e38dd3b3503b78fccedae80053626d8649c68346c7c49c" + } + ], + [], + [ + { + "op": "add", + "path": "/hash", + "value": "0x466ae7d4b7c270121f0a8d68fbc6c9091ffc4aa976a553a5bfa56a79cf9f63dd", + "note": "transactions#setTxHash", + "timestamp": 1528133227534 + } + ], + [ + { + "op": "add", + "path": "/submittedTime", + "value": 1528133227538, + "note": "txStateManager - add submitted time stamp", + "timestamp": 1528133227538 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "submitted", + "note": "txStateManager: setting status to submitted", + "timestamp": 1528133227543 + } + ], + [ + { + "op": "add", + "path": "/firstRetryBlockNumber", + "value": "0x24af72", + "note": "transactions/pending-tx-tracker#event: tx:block-update", + "timestamp": 1528133238980 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "confirmed", + "note": "txStateManager: setting status to confirmed", + "timestamp": 1528133255035 + } + ] + ], + "origin": "crypko.ai", + "rawTx": "0xf8b181b8843b9aca008306169e94fe2149773b3513703e79ad23d05a778a185016ee870aa87bee538000b844ea94496b000000000000000000000000000000000000000000000000000000000001e1eb000000000000000000000000000000000000000000000000000000000001de332ca07bb2efbb8529d67606f9f89e7934c594a31d50c7d24a3286c20a2944a3b8c2a9a07b55ebd8aa28728ce0e38dd3b3503b78fccedae80053626d8649c68346c7c49c", + "hash": "0x466ae7d4b7c270121f0a8d68fbc6c9091ffc4aa976a553a5bfa56a79cf9f63dd", + "submittedTime": 1528133227538, + "firstRetryBlockNumber": "0x24af72" + }, + { + "id": 3387511061307740, + "time": 1528133291381, + "status": "confirmed", + "chainId": "0x5", + "loadingDefaults": false, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d", + "value": "0x0", + "data": "0xa9059cbb00000000000000000000000092e659448c48fc926ec942d0da1459260d36bb330000000000000000000000000000000000000000000000000000000000000002", + "gas": "0xd508", + "gasPrice": "0x3b9aca00", + "nonce": "0xb9" + }, + "history": [ + { + "id": 3387511061307740, + "time": 1528133291381, + "status": "unapproved", + "chainId": "0x5", + "loadingDefaults": true, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d", + "value": "0x0", + "data": "0xa9059cbb00000000000000000000000092e659448c48fc926ec942d0da1459260d36bb330000000000000000000000000000000000000000000000000000000000000002", + "gas": "0xd508", + "gasPrice": "0x3b9aca00" + } + }, + [ + { + "op": "replace", + "path": "/loadingDefaults", + "value": false, + "timestamp": 1528133291486 + } + ], + [ + { + "op": "add", + "path": "/origin", + "value": "MetaMask", + "note": "#newUnapprovedTransaction - adding the origin", + "timestamp": 1528133291486 + } + ], + [], + [ + { + "op": "replace", + "path": "/status", + "value": "approved", + "note": "txStateManager: setting status to approved", + "timestamp": 1528133293588 + } + ], + [ + { + "op": "add", + "path": "/txParams/nonce", + "value": "0xb9", + "note": "transactions#approveTransaction", + "timestamp": 1528133293706 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "signed", + "note": "transactions#publishTransaction", + "timestamp": 1528133293724 + }, + { + "op": "add", + "path": "/rawTx", + "value": "0xf8a981b9843b9aca0082d50894108cf70c7d384c552f42c07c41c0e1e46d77ea0d80b844a9059cbb00000000000000000000000092e659448c48fc926ec942d0da1459260d36bb3300000000000000000000000000000000000000000000000000000000000000022ca04f05310490d3e3a9a159ae25f52cec9afb0a69527d30be832aaae12e64ff056ea075f81a5220bed481e764bab8830c57169c59fe528ca9cf3442f47f7618a9b4a9" + } + ], + [], + [ + { + "op": "add", + "path": "/hash", + "value": "0x3680dc9815cd05b620b6dd0017d949604ca7d92f051d5542fc8a5ecaa876af09", + "note": "transactions#setTxHash", + "timestamp": 1528133293853 + } + ], + [ + { + "op": "add", + "path": "/submittedTime", + "value": 1528133293859, + "note": "txStateManager - add submitted time stamp", + "timestamp": 1528133293862 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "submitted", + "note": "txStateManager: setting status to submitted", + "timestamp": 1528133293867 + } + ], + [ + { + "op": "add", + "path": "/firstRetryBlockNumber", + "value": "0x24af76", + "note": "transactions/pending-tx-tracker#event: tx:block-update", + "timestamp": 1528133295200 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "confirmed", + "note": "txStateManager: setting status to confirmed", + "timestamp": 1528133327522 + } + ] + ], + "origin": "MetaMask", + "rawTx": "0xf8a981b9843b9aca0082d50894108cf70c7d384c552f42c07c41c0e1e46d77ea0d80b844a9059cbb00000000000000000000000092e659448c48fc926ec942d0da1459260d36bb3300000000000000000000000000000000000000000000000000000000000000022ca04f05310490d3e3a9a159ae25f52cec9afb0a69527d30be832aaae12e64ff056ea075f81a5220bed481e764bab8830c57169c59fe528ca9cf3442f47f7618a9b4a9", + "hash": "0x3680dc9815cd05b620b6dd0017d949604ca7d92f051d5542fc8a5ecaa876af09", + "submittedTime": 1528133293859, + "firstRetryBlockNumber": "0x24af76" + }, + { + "id": 3387511061307741, + "time": 1528133318440, + "status": "rejected", + "chainId": "0x5", + "loadingDefaults": false, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "value": "0x0", + "gasPrice": "0x3b9aca00", + "gas": "0x5208" + }, + "history": [ + { + "id": 3387511061307741, + "time": 1528133318440, + "status": "unapproved", + "chainId": "0x5", + "loadingDefaults": true, + "txParams": { + "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62", + "to": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62" + } + }, + [ + { + "op": "add", + "path": "/txParams/value", + "value": "0x0", + "timestamp": 1528133319641 + }, + { + "op": "add", + "path": "/txParams/gasPrice", + "value": "0x3b9aca00" + }, + { + "op": "add", + "path": "/txParams/gas", + "value": "0x5208" + }, + { + "op": "replace", + "path": "/loadingDefaults", + "value": false + } + ], + [ + { + "op": "add", + "path": "/origin", + "value": "tmashuang.github.io", + "note": "#newUnapprovedTransaction - adding the origin", + "timestamp": 1528133319642 + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "rejected", + "note": "txStateManager: setting status to rejected", + "timestamp": 1528133320924 + } + ] + ], + "origin": "tmashuang.github.io" + } + ], + "trezorModel": null, + "unapprovedDecryptMsgCount": 0, + "unapprovedDecryptMsgs": {}, + "unapprovedEncryptionPublicKeyMsgCount": 0, + "unapprovedEncryptionPublicKeyMsgs": {}, + "unapprovedMsgCount": 0, + "unapprovedMsgs": {}, + "unapprovedPersonalMsgCount": 0, + "unapprovedPersonalMsgs": {}, + "unapprovedTypedMessages": {}, + "unapprovedTypedMessagesCount": 0, + "unconnectedAccountAlertShownOrigins": {}, + "unencryptedSnapStates": {}, + "use4ByteResolution": true, + "useAddressBarEnsResolution": true, + "useBlockie": false, + "useCurrencyRateCheck": true, + "useExternalNameSources": true, + "useMultiAccountBalanceChecker": false, + "useNftDetection": false, + "useNonceField": false, + "usePhishDetect": true, + "useRequestQueue": false, + "useSafeChainsListValidation": true, + "useTokenDetection": false, + "useTransactionSimulations": true, + "usedNetworks": { + "0x5": { + "rpcUrl": "https://goerli.infura.io/v3/8f8f7f9f6f4c4f3e8f8f7f9f6f4c4f3e", + "chainId": "0x5", + "nickname": "Goerli Test Network", + "ticker": "ETH", + "blockExplorerUrl": "https://goerli.etherscan.io" + } + }, + "userOperations": {}, + "versionFileETag": "W/\"598946a37f16c5b882a0bebbadf4509f\"", + "versionInfo": [], + "web3ShimUsageOrigins": {}, + "welcomeScreenSeen": false +} diff --git a/test/integration/data/onboarding-completion-route.json b/test/integration/data/onboarding-completion-route.json new file mode 100644 index 000000000000..1ec4806bf435 --- /dev/null +++ b/test/integration/data/onboarding-completion-route.json @@ -0,0 +1,454 @@ +{ + "accounts": { "0x03cf1158b58ccdfc04dd518f11f85c3ee7fa0189": {} }, + "accountsByChainId": {}, + "addSnapAccountEnabled": false, + "addressBook": { + "*": { + "0x03Cf1158B58CcDfc04dd518F11f85C3ee7fA0189": { + "address": "0x03Cf1158B58CcDfc04dd518F11f85C3ee7fA0189", + "chainId": "*", + "isEns": false, + "memo": "", + "name": "Account 1" + } + } + }, + "advancedGasFee": {}, + "alertEnabledness": { "unconnectedAccount": true, "web3ShimUsage": true }, + "allDetectedTokens": {}, + "allIgnoredTokens": {}, + "allNftContracts": {}, + "allNfts": {}, + "allTokens": {}, + "announcements": { + "8": { "id": 8, "date": "2021-11-01", "isShown": false }, + "20": { "id": 20, "date": null, "isShown": false }, + "24": { "id": 24, "date": null, "isShown": false }, + "25": { "id": 25, "date": null, "isShown": false }, + "28": { "id": 28, "date": null, "isShown": false }, + "29": { "id": 29, "date": null, "isShown": false }, + "30": { + "id": 30, + "date": null, + "image": { + "src": "images/staking-light-mode-preview.png", + "width": "100%" + }, + "isShown": false + }, + "31": { + "id": 31, + "date": null, + "image": { + "src": "images/petnames-whatsnew-banner.svg", + "width": "100%" + }, + "isShown": false + }, + "32": { + "id": 32, + "date": null, + "image": { + "src": "images/portfolio-v2-whatsnew-banner.png", + "width": "100%" + }, + "isShown": false + }, + "33": { "id": 33, "date": "2024-04-09", "isShown": false } + }, + "approvalFlows": [], + "browserEnvironment": { "os": "mac", "browser": "firefox" }, + "completedOnboarding": false, + "confirmationExchangeRates": {}, + "connectedStatusPopoverHasBeenShown": true, + "contractExchangeRates": {}, + "contractExchangeRatesByChainId": {}, + "currencyRates": { + "ETH": { + "conversionDate": 0, + "conversionRate": 0, + "usdConversionRate": null + } + }, + "currentAppVersion": "11.14.4-flask.0", + "currentBlockGasLimit": "", + "currentBlockGasLimitByChainId": {}, + "currentCurrency": "usd", + "currentExtensionPopupId": 1715943310719, + "currentLocale": "en", + "currentMigrationVersion": 119, + "customNonceValue": "", + "database": null, + "databaseUnavailable": false, + "defaultHomeActiveTabName": null, + "desktopEnabled": false, + "detectedTokens": [], + "disabledRpcMethodPreferences": { "eth_sign": false }, + "dismissSeedBackUpReminder": false, + "domains": {}, + "enableMV3TimestampSave": true, + "encryptionKey": "{\"key\":{\"alg\":\"A256GCM\",\"ext\":true,\"k\":\"IWP6tqC2WeYfTxhHhTFU2m1hhO9GD5YhgYe2iRzu5GU\",\"key_ops\":[\"encrypt\",\"decrypt\"],\"kty\":\"oct\"},\"derivationOptions\":{\"algorithm\":\"PBKDF2\",\"params\":{\"iterations\":600000}}}", + "encryptionSalt": "6AA1VP9N1aU5bglaSiAhmE9PdLH3YCqiKJagv+wQCR8=", + "ensEntries": {}, + "ensResolutionsByAddress": {}, + "estimatedGasFeeTimeBounds": {}, + "eventsBeforeMetricsOptIn": [], + "featureFlags": {}, + "firstTimeFlowType": "create", + "forgottenPassword": false, + "fragments": {}, + "fullScreenGasPollTokens": [], + "gasEstimateType": "none", + "gasFeeEstimates": {}, + "gasFeeEstimatesByChainId": {}, + "hadAdvancedGasFeesSetPriorToMigration92_3": false, + "hasDismissedOpenSeaToBlockaidBanner": false, + "hiddenAccountList": [], + "identities": { + "0x03cf1158b58ccdfc04dd518f11f85c3ee7fa0189": { + "name": "Account 1", + "address": "0x03cf1158b58ccdfc04dd518f11f85c3ee7fa0189", + "lastSelected": 1715944041809 + } + }, + "ignoredNfts": [], + "ignoredTokens": [], + "incomingTransactionsPreferences": { + "0x1": true, + "0xe708": true, + "0x5": true, + "0xaa36a7": true, + "0xe705": true + }, + "interfaces": {}, + "internalAccounts": { + "accounts": { + "51737ecb-beb4-4a2b-bcab-c99d0f587a33": { + "id": "51737ecb-beb4-4a2b-bcab-c99d0f587a33", + "address": "0x03cf1158b58ccdfc04dd518f11f85c3ee7fa0189", + "options": {}, + "methods": [ + "personal_sign", + "eth_sign", + "eth_signTransaction", + "eth_signTypedData_v1", + "eth_signTypedData_v3", + "eth_signTypedData_v4" + ], + "type": "eip155:eoa", + "metadata": { + "name": "Account 1", + "keyring": { "type": "HD Key Tree" }, + "lastSelected": 1715944041814 + } + } + }, + "selectedAccount": "51737ecb-beb4-4a2b-bcab-c99d0f587a33" + }, + "ipfsGateway": "dweb.link", + "isAccountMenuOpen": false, + "isInitialized": true, + "isIpfsGatewayEnabled": true, + "isNetworkMenuOpen": false, + "isUnlocked": true, + "jobs": {}, + "keyrings": [ + { + "type": "HD Key Tree", + "accounts": ["0x03cf1158b58ccdfc04dd518f11f85c3ee7fa0189"] + } + ], + "knownMethodData": {}, + "lastFetchedBlockNumbers": {}, + "lastUpdated": null, + "ledgerTransportType": "u2f", + "logs": {}, + "lostIdentities": {}, + "metaMetricsId": "0x4d6d78a255217af6411a5bbd39e31b5e46e0e920bdf7e979470f316cbe8c00eb", + "methodData": {}, + "nameSources": {}, + "names": { + "ethereumAddress": { + "0x03cf1158b58ccdfc04dd518f11f85c3ee7fa0189": { + "*": { + "proposedNames": {}, + "name": "Account 1", + "sourceId": null, + "origin": "account-identity" + } + } + } + }, + "networkConfigurations": {}, + "networksMetadata": { "sepolia": { "status": "unknown", "EIPS": {} } }, + "nextNonce": null, + "nftsDetectionNoticeDismissed": false, + "nftsDropdownState": {}, + "nonRPCGasFeeApisDisabled": false, + "notificationGasPollTokens": [], + "notifications": {}, + "onboardingTabs": {}, + "openSeaEnabled": false, + "orderedNetworkList": [], + "outdatedBrowserWarningLastShown": 1715943225448, + "participateInMetaMetrics": true, + "pendingApprovalCount": 0, + "pendingApprovals": {}, + "pendingTokens": {}, + "permissionActivityLog": [], + "permissionHistory": {}, + "pinnedAccountList": [], + "popupGasPollTokens": [], + "preferences": { + "showExtensionInFullSizeView": false, + "showFiatInTestnets": false, + "showTestNetworks": false, + "smartTransactionsOptInStatus": null, + "useNativeCurrencyAsPrimaryCurrency": true, + "hideZeroBalanceTokens": false, + "petnamesEnabled": true, + "redesignedConfirmationsEnabled": true, + "featureNotificationsEnabled": false + }, + "preventPollingOnNetworkRestart": false, + "previousAppVersion": "", + "previousMigrationVersion": 0, + "previousUserTraits": { + "address_book_entries": 1, + "install_date_ext": "", + "ledger_connection_type": "u2f", + "networks_added": [], + "networks_without_ticker": [], + "nft_autodetection_enabled": false, + "number_of_accounts": 1, + "number_of_nft_collections": 0, + "number_of_nfts": 0, + "number_of_tokens": 0, + "undefined": false, + "three_box_enabled": false, + "theme": "os", + "token_detection_enabled": true, + "desktop_enabled": false, + "security_providers": ["blockaid"], + "petname_addresses_count": 1 + }, + "providerConfig": { + "type": "sepolia", + "chainId": "0xaa36a7", + "ticker": "SepoliaETH" + }, + "qrHardware": {}, + "recoveryPhraseReminderHasBeenShown": false, + "recoveryPhraseReminderLastShown": 1715943225448, + "securityAlertsEnabled": true, + "seedPhraseBackedUp": true, + "segmentApiCalls": { + "6QHUuopP26ok11pDgrCt": { + "eventType": "identify", + "payload": { + "userId": "0x4d6d78a255217af6411a5bbd39e31b5e46e0e920bdf7e979470f316cbe8c00eb", + "traits": { + "address_book_entries": 1, + "number_of_accounts": 1, + "petname_addresses_count": 1 + }, + "messageId": "6QHUuopP26ok11pDgrCt", + "timestamp": "Fri May 17 2024 12:07:22 GMT+0100 (Western European Summer Time)" + } + } + }, + "selectedAddress": "0x03cf1158b58ccdfc04dd518f11f85c3ee7fa0189", + "selectedNetworkClientId": "sepolia", + "showAccountBanner": true, + "showBetaHeader": false, + "showNetworkBanner": true, + "showPermissionsTour": true, + "showTestnetMessageInDropdown": true, + "signatureSecurityAlertResponses": {}, + "smartTransactionsState": { + "smartTransactions": { "0x1": [] }, + "fees": {}, + "liveness": true, + "livenessByChainId": { "0x1": true, "0xaa36a7": true }, + "feesByChainId": { "0x1": {}, "0xaa36a7": {} } + }, + "snapRegistryList": {}, + "snapStates": {}, + "snaps": { + "npm:@metamask/message-signing-snap": { + "blocked": false, + "enabled": true, + "removable": false, + "preinstalled": true, + "id": "npm:@metamask/message-signing-snap", + "initialPermissions": { + "snap_getEntropy": {}, + "endowment:rpc": { "dapps": true, "snaps": false } + }, + "manifest": { + "version": "0.3.3", + "description": "Automatic message signing of MetaMask tagged messages", + "proposedName": "MetaMask Message Signing Snap", + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/message-signing-snap.git" + }, + "source": { + "shasum": "ol44wik35znt/n3Z7GGyXjh+J0M00yDM7YZOYQegA7I=", + "location": { + "npm": { + "filePath": "dist/bundle.js", + "iconPath": "images/icon.svg", + "packageName": "@metamask/message-signing-snap", + "registry": "https://registry.npmjs.org/" + } + } + }, + "initialPermissions": { + "snap_getEntropy": {}, + "endowment:rpc": { "dapps": true, "snaps": false } + }, + "manifestVersion": "0.1" + }, + "status": "stopped", + "version": "0.3.3", + "versionHistory": [ + { "version": "0.3.3", "date": 1715943225450, "origin": "metamask" } + ], + "localizationFiles": [] + } + }, + "snapsAddSnapAccountModalDismissed": false, + "storageMetadata": [], + "subjectMetadata": { + "npm:@metamask/message-signing-snap": { + "subjectType": "snap", + "name": "MetaMask Message Signing Snap", + "origin": "npm:@metamask/message-signing-snap", + "version": "0.3.3", + "svgIcon": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "extensionId": null, + "iconUrl": null + }, + "https://dco-assets.everestads.net": { + "iconUrl": null, + "name": "Ad", + "subjectType": "website", + "origin": "https://dco-assets.everestads.net", + "extensionId": null + }, + "https://consensys.okta.com": { + "iconUrl": "https://consensys.okta.com/favicon.ico", + "name": "Consensys - Sign In", + "subjectType": "website", + "origin": "https://consensys.okta.com", + "extensionId": null + }, + "https://5c4669a19fd4b55728637d0c236c6cea.safeframe.googlesyndication.com": { + "iconUrl": null, + "name": "SafeFrame Container", + "subjectType": "website", + "origin": "https://5c4669a19fd4b55728637d0c236c6cea.safeframe.googlesyndication.com", + "extensionId": null + }, + "https://start.metamask.io": { + "iconUrl": null, + "name": "Start | MetaMask", + "subjectType": "website", + "origin": "https://start.metamask.io", + "extensionId": null + } + }, + "subjects": { + "npm:@metamask/message-signing-snap": { + "origin": "npm:@metamask/message-signing-snap", + "permissions": { + "snap_getEntropy": { + "id": "ggXAHjzFuHpf3zsP6g5Jf", + "parentCapability": "snap_getEntropy", + "invoker": "npm:@metamask/message-signing-snap", + "caveats": null, + "date": 1715943225451 + }, + "endowment:rpc": { + "id": "fNwGYVVOtD-NgTat1-xmO", + "parentCapability": "endowment:rpc", + "invoker": "npm:@metamask/message-signing-snap", + "caveats": [ + { "type": "rpcOrigin", "value": { "dapps": true, "snaps": false } } + ], + "date": 1715943225451 + } + } + } + }, + "surveyLinkLastClickedOrClosed": null, + "swapsState": { + "quotes": {}, + "quotesPollingLimitEnabled": false, + "fetchParams": null, + "tokens": null, + "tradeTxId": null, + "approveTxId": null, + "quotesLastFetched": null, + "customMaxGas": "", + "customGasPrice": null, + "customMaxFeePerGas": null, + "customMaxPriorityFeePerGas": null, + "swapsUserFeeLevel": "", + "selectedAggId": null, + "customApproveTxData": "", + "errorKey": "", + "topAggId": null, + "routeState": "", + "swapsFeatureIsLive": true, + "saveFetchedQuotes": false, + "swapsQuoteRefreshTime": 60000, + "swapsQuotePrefetchingRefreshTime": 60000, + "swapsStxBatchStatusRefreshTime": 10000, + "swapsStxGetTransactionsRefreshTime": 10000, + "swapsStxMaxFeeMultiplier": 2 + }, + "switchedNetworkDetails": null, + "switchedNetworkNeverShowMessage": false, + "termsOfUseLastAgreed": 1715944006161, + "theme": "os", + "tokenList": {}, + "tokens": [], + "tokensChainsCache": {}, + "traits": {}, + "transactions": [], + "trezorModel": null, + "unapprovedDecryptMsgCount": 0, + "unapprovedDecryptMsgs": {}, + "unapprovedEncryptionPublicKeyMsgCount": 0, + "unapprovedEncryptionPublicKeyMsgs": {}, + "unapprovedMsgCount": 0, + "unapprovedMsgs": {}, + "unapprovedPersonalMsgCount": 0, + "unapprovedPersonalMsgs": {}, + "unapprovedTypedMessages": {}, + "unapprovedTypedMessagesCount": 0, + "unconnectedAccountAlertShownOrigins": {}, + "unencryptedSnapStates": {}, + "use4ByteResolution": true, + "useAddressBarEnsResolution": true, + "useBlockie": false, + "useCurrencyRateCheck": true, + "useExternalNameSources": true, + "useExternalServices": true, + "useMultiAccountBalanceChecker": true, + "useNftDetection": false, + "useNonceField": false, + "usePhishDetect": true, + "useRequestQueue": true, + "useSafeChainsListValidation": true, + "useTokenDetection": true, + "useTransactionSimulations": true, + "usedNetworks": { "0x1": true, "0x5": true, "0x539": true }, + "userOperations": {}, + "versionFileETag": "", + "versionInfo": [], + "web3ShimUsageOrigins": {}, + "welcomeScreenSeen": false +} diff --git a/test/integration/onboarding/wallet-created.test.tsx b/test/integration/onboarding/wallet-created.test.tsx new file mode 100644 index 000000000000..2a5983e60430 --- /dev/null +++ b/test/integration/onboarding/wallet-created.test.tsx @@ -0,0 +1,117 @@ +import { fireEvent, waitFor } from '@testing-library/react'; +import mockMetaMaskState from '../data/onboarding-completion-route.json'; +import { integrationTestRender } from '../../lib/render-helpers'; +import * as backgroundConnection from '../../../ui/store/background-connection'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, +} from '../../../shared/constants/metametrics'; + +jest.mock('../../../ui/store/background-connection', () => ({ + ...jest.requireActual('../../../ui/store/background-connection'), + submitRequestToBackground: jest.fn(), +})); + +const mockedBackgroundConnection = jest.mocked(backgroundConnection); + +const backgroundConnectionMocked = { + onNotification: jest.fn(), +}; + +describe('Wallet Created Events', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('are sent when onboarding user who chooses to opt in metrics', async () => { + const { getByTestId, getByText } = await integrationTestRender({ + preloadedState: mockMetaMaskState, + backgroundConnection: backgroundConnectionMocked, + }); + + expect(getByText('Wallet creation successful')).toBeInTheDocument(); + + fireEvent.click(getByTestId('onboarding-complete-done')); + + await waitFor(() => { + expect(getByTestId('onboarding-pin-extension')).toBeInTheDocument(); + }); + + let confirmAccountDetailsModalMetricsEvent; + + await waitFor(() => { + confirmAccountDetailsModalMetricsEvent = + mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( + (call) => call[0] === 'trackMetaMetricsEvent', + ); + + expect(confirmAccountDetailsModalMetricsEvent?.[0]).toBe( + 'trackMetaMetricsEvent', + ); + }); + + expect(confirmAccountDetailsModalMetricsEvent?.[1]).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + category: MetaMetricsEventCategory.Onboarding, + event: MetaMetricsEventName.OnboardingWalletCreationComplete, + properties: { + method: mockMetaMaskState.firstTimeFlowType, + }, + }), + ]), + ); + + fireEvent.click(getByTestId('pin-extension-next')); + + await waitFor(() => { + expect( + getByText( + `Pin MetaMask on your browser so it's accessible and easy to view transaction confirmations.`, + ), + ).toBeInTheDocument(); + }); + + fireEvent.click(getByTestId('pin-extension-done')); + + await waitFor(() => { + const completeOnboardingBackgroundRequest = + mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( + (call) => call[0] === 'completeOnboarding', + ); + + expect(completeOnboardingBackgroundRequest).toBeTruthy(); + }); + + await waitFor(() => { + const OnboardingWalletSetupCompleteEvent = + mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( + (call) => { + if (call[0] === 'trackMetaMetricsEvent') { + const callArgs = call[1] as unknown as Record[]; + + return ( + callArgs[0].event === + MetaMetricsEventName.OnboardingWalletSetupComplete + ); + } + + return false; + }, + ); + + expect(OnboardingWalletSetupCompleteEvent?.[1]).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + category: MetaMetricsEventCategory.Onboarding, + event: MetaMetricsEventName.OnboardingWalletSetupComplete, + properties: { + wallet_setup_type: 'new', + new_wallet: true, + }, + }), + ]), + ); + }); + }); +}); diff --git a/test/jest/mock-store.js b/test/jest/mock-store.js index 85f617450136..ad0e6adc7f07 100644 --- a/test/jest/mock-store.js +++ b/test/jest/mock-store.js @@ -650,6 +650,7 @@ export const createSwapsMockStore = () => { ], }, }, + balances: {}, }, appState: { modal: { diff --git a/test/jest/setup.js b/test/jest/setup.js index e4fb03adad3a..0ee19a4d61b8 100644 --- a/test/jest/setup.js +++ b/test/jest/setup.js @@ -1,15 +1,5 @@ // This file is for Jest-specific setup only and runs before our Jest tests. - -import nock from 'nock'; -import '@testing-library/jest-dom'; - -jest.mock('webextension-polyfill', () => { - return { - runtime: { - getManifest: () => ({ manifest_version: 2 }), - }, - }; -}); +import '../helpers/setup-after-helper'; jest.mock('../../ui/hooks/usePetnamesEnabled', () => ({ usePetnamesEnabled: () => false, @@ -43,11 +33,6 @@ function treatUnresolvedAfter(duration) { }); } -/* eslint-disable-next-line jest/require-top-level-describe */ -beforeEach(() => { - nock.cleanAll(); -}); - expect.extend({ /** * Tests that the given promise is fulfilled within a certain amount of time @@ -136,6 +121,3 @@ expect.extend({ }; }, }); - -// Setup window.prompt -global.prompt = () => undefined; diff --git a/test/lib/render-helpers.js b/test/lib/render-helpers.js index e77e8418c2e1..fa54ac1fb4ec 100644 --- a/test/lib/render-helpers.js +++ b/test/lib/render-helpers.js @@ -11,6 +11,8 @@ import { I18nContext, LegacyI18nProvider } from '../../ui/contexts/i18n'; import { LegacyMetaMetricsProvider } from '../../ui/contexts/metametrics'; import { getMessage } from '../../ui/helpers/utils/i18n-helper'; import * as en from '../../app/_locales/en/messages.json'; +import { setupInitialStore } from '../../ui'; +import Root from '../../ui/pages'; export const I18nProvider = (props) => { const { currentLocale, current, en: eng } = props; @@ -133,3 +135,36 @@ export function renderWithUserEvent(jsx) { ...render(jsx), }; } + +/** + * Helper function to render the UI application for integration tests. + * It uses the Root component and sets up the store with the provided preloaded state. + * + * @param {*} extendedRenderOptions + * @param {*} extendedRenderOptions.preloadedState - The initial state used to initialised the redux store. For integration tests we rely on a real store instance following the redux recommendations - https://redux.js.org/usage/writing-tests#guiding-principles + * @param {*} extendedRenderOptions.backgroundConnection - The background connection rpc method. When writing integration tests, we can pass a mock background connection to simulate the background connection methods. + * @param {*} extendedRenderOptions.activeTab - The active tab object. + * @returns The rendered result from testing library. + */ +export async function integrationTestRender(extendedRenderOptions) { + const { + preloadedState = {}, + backgroundConnection, + activeTab = { + id: 113, + title: 'E2E Test Dapp', + origin: 'https://metamask.github.io', + protocol: 'https:', + url: 'https://metamask.github.io/test-dapp/', + }, + ...renderOptions + } = extendedRenderOptions; + + const store = await setupInitialStore(preloadedState, backgroundConnection, { + activeTab, + }); + + return { + ...render(, { ...renderOptions }), + }; +} diff --git a/test/manual-scenarios/incoming-transactions/receive native token.csv b/test/manual-scenarios/incoming-transactions/receive native token.csv index c02a6f17180f..a35593205cd0 100644 --- a/test/manual-scenarios/incoming-transactions/receive native token.csv +++ b/test/manual-scenarios/incoming-transactions/receive native token.csv @@ -12,7 +12,7 @@ The selected network is Sepolia.", 7,Close the settings page.,,The overview page is displayed. , 8,Open the Account menu.,,The account menu popover is shown., 9,Click the Add account button.,,The Add account popover is shown., -10,Click the Add a new account button.,,, +10,Click the Add a new Ethereum account button.,,, 11,Specify an account name and create the account.,e.g. Account 2.,The extension switches to Account 2., 12,Open the Account menu.,,The account menu popover is shown., 13,Switch back to the initial account.,e.g. Account 1.,The extension switches to Account 1., diff --git a/test/setup.js b/test/setup.js index 17f9c45249ab..b521d2274048 100644 --- a/test/setup.js +++ b/test/setup.js @@ -3,11 +3,6 @@ require('ts-node').register({ transpileOnly: true }); require('./helpers/setup-helper'); -window.SVGPathElement = window.SVGPathElement || { prototype: {} }; -global.indexedDB = {}; -// scrollIntoView is not available in JSDOM -window.HTMLElement.prototype.scrollIntoView = () => undefined - global.platform = { // Required for: coin overviews components openTab: () => undefined, diff --git a/ui/__mocks__/actions.js b/ui/__mocks__/actions.js index 0da54a4512e8..c3c20ba70ad1 100644 --- a/ui/__mocks__/actions.js +++ b/ui/__mocks__/actions.js @@ -1,7 +1,16 @@ const actions = require('../store/actions'); +const { + CONTRACT_ADDRESS_UNISWAP, + CONTRACT_ADDRESS_SOURCIFY, + CONTRACT_ADDRESS_FOUR_BYTE, + TRANSACTION_DECODE_UNISWAP, + TRANSACTION_DECODE_SOURCIFY, + TRANSACTION_DECODE_FOUR_BYTE, +} = require('../../test/data/confirmations/transaction-decode'); const ERC20_TOKEN_1_MOCK = '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599'; // WBTC const ERC20_TOKEN_2_MOCK = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; // USDC + const TOKEN_DETAILS_MOCK = { [ERC20_TOKEN_1_MOCK]: { address: ERC20_TOKEN_1_MOCK, @@ -21,10 +30,33 @@ module.exports = { ...actions, getTokenStandardAndDetails: (address) => { if (!TOKEN_DETAILS_MOCK[address]) { - return Promise.reject(new Error(`Token not found: ${address}`)); + console.log('Token not found:', address); + + return Promise.resolve({ + address, + standard: 'ERC20', + decimals: 18, + name: 'Missing Mock', + }); } + return Promise.resolve(TOKEN_DETAILS_MOCK[address]); }, + // eslint-disable-next-line no-empty-function trackMetaMetricsEvent: () => {}, + + decodeTransactionData: async (request) => { + const { contractAddress } = request; + + if (contractAddress === CONTRACT_ADDRESS_UNISWAP) { + return TRANSACTION_DECODE_UNISWAP; + } else if (contractAddress === CONTRACT_ADDRESS_SOURCIFY) { + return TRANSACTION_DECODE_SOURCIFY; + } else if (contractAddress === CONTRACT_ADDRESS_FOUR_BYTE) { + return TRANSACTION_DECODE_FOUR_BYTE; + } + + return undefined; + }, }; diff --git a/ui/__mocks__/react-router-dom.js b/ui/__mocks__/react-router-dom.js deleted file mode 100644 index 1f65e3395165..000000000000 --- a/ui/__mocks__/react-router-dom.js +++ /dev/null @@ -1,13 +0,0 @@ -const originalModule = jest.requireActual('react-router-dom'); - -module.exports = { - ...originalModule, - useHistory: jest.fn(() => { - return []; - }), - useLocation: jest.fn(() => { - return { - pathname: '/swaps/build-quote', - }; - }), -}; diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss index 63c916a5b256..a66133f575a7 100644 --- a/ui/components/app/app-components.scss +++ b/ui/components/app/app-components.scss @@ -72,6 +72,7 @@ @import 'network-account-balance-header/index'; @import 'name/index'; @import 'name/name-details/index'; +@import 'confirm/info/row/index'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) @import '../institutional/interactive-replacement-token-notification/index'; @import '../institutional/confirm-remove-jwt-modal/index'; diff --git a/ui/components/app/asset-list/asset-list.js b/ui/components/app/asset-list/asset-list.js index 00b4d24a3b64..b9c200233c04 100644 --- a/ui/components/app/asset-list/asset-list.js +++ b/ui/components/app/asset-list/asset-list.js @@ -19,6 +19,10 @@ import { getMultichainCurrencyImage, getMultichainIsMainnet, getMultichainSelectedAccountCachedBalance, + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) + getMultichainIsBitcoin, + ///: END:ONLY_INCLUDE_IF + getMultichainSelectedAccountCachedBalanceIsZero, } from '../../../selectors/multichain'; import { useCurrencyDisplay } from '../../../hooks/useCurrencyDisplay'; import { MetaMetricsContext } from '../../../contexts/metametrics'; @@ -98,24 +102,33 @@ const AssetList = ({ onClickAsset, showTokensLinks }) => { getIstokenDetectionInactiveOnNonMainnetSupportedNetwork, ); - const { tokensWithBalances, totalFiatBalance, loading } = - useAccountTotalFiatBalance(selectedAccount, shouldHideZeroBalanceTokens); + const { tokensWithBalances, loading } = useAccountTotalFiatBalance( + selectedAccount, + shouldHideZeroBalanceTokens, + ); tokensWithBalances.forEach((token) => { // token.string is the balance displayed in the TokenList UI token.string = roundToDecimalPlacesRemovingExtraZeroes(token.string, 5); }); - const balanceIsZero = Number(totalFiatBalance) === 0; + + const balanceIsZero = useSelector( + getMultichainSelectedAccountCachedBalanceIsZero, + ); + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) const isBuyableChain = useSelector(getIsNativeTokenBuyable); const shouldShowBuy = isBuyableChain && balanceIsZero; ///: END:ONLY_INCLUDE_IF const isEvm = useSelector(getMultichainIsEvm); - // NOTE: Since we can parametrize it now, we keep the original behavior // for EVM assets const shouldShowTokensLinks = showTokensLinks ?? isEvm; + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) + const isBtc = useSelector(getMultichainIsBitcoin); + ///: END:ONLY_INCLUDE_IF + let isStakeable = isMainnet && isEvm; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) isStakeable = false; @@ -133,7 +146,13 @@ const AssetList = ({ onClickAsset, showTokensLinks }) => { { ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) shouldShowBuy ? ( - + ) : null ///: END:ONLY_INCLUDE_IF } diff --git a/ui/components/app/confirm/info/row/__snapshots__/expandable-row.test.tsx.snap b/ui/components/app/confirm/info/row/__snapshots__/expandable-row.test.tsx.snap new file mode 100644 index 000000000000..db8407fd7bb9 --- /dev/null +++ b/ui/components/app/confirm/info/row/__snapshots__/expandable-row.test.tsx.snap @@ -0,0 +1,45 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ConfirmInfoExpandableRow should match snapshot 1`] = ` +
+
+
+

+ Expandable Row +

+
+
+ Expandable Value + +
+
+ +
+`; diff --git a/ui/components/app/confirm/info/row/currency.tsx b/ui/components/app/confirm/info/row/currency.tsx index 3de8060cec5e..82ce82c3a113 100644 --- a/ui/components/app/confirm/info/row/currency.tsx +++ b/ui/components/app/confirm/info/row/currency.tsx @@ -13,7 +13,7 @@ import CurrencyDisplay from '../../../../ui/currency-display/currency-display.co export type ConfirmInfoRowCurrencyProps = { value: number | string; currency?: string; - dataTestId?: string; + 'data-testid'?: string; }; // todo: the component currently takes care of displaying value in the currency passed @@ -23,7 +23,7 @@ export type ConfirmInfoRowCurrencyProps = { export const ConfirmInfoRowCurrency = ({ value, currency, - dataTestId, + 'data-testid': dataTestId, }: ConfirmInfoRowCurrencyProps) => ( ( +
+ {story()} +
+ ), + ], +}; + +export const DefaultStory = () => ( + + + + + + + + + } + > + + +); diff --git a/ui/components/app/confirm/info/row/expandable-row.test.tsx b/ui/components/app/confirm/info/row/expandable-row.test.tsx new file mode 100644 index 000000000000..6ee5b40670f7 --- /dev/null +++ b/ui/components/app/confirm/info/row/expandable-row.test.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { ConfirmInfoExpandableRow } from './expandable-row'; + +describe('ConfirmInfoExpandableRow', () => { + it('should match snapshot', () => { + const { container } = render( + + Expandable Value + , + ); + + expect(container).toMatchSnapshot(); + }); +}); diff --git a/ui/components/app/confirm/info/row/expandable-row.tsx b/ui/components/app/confirm/info/row/expandable-row.tsx new file mode 100644 index 000000000000..23fa7f7d5fd0 --- /dev/null +++ b/ui/components/app/confirm/info/row/expandable-row.tsx @@ -0,0 +1,73 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import classnames from 'classnames'; +import { + Box, + ButtonIcon, + ButtonIconSize, + IconName, +} from '../../../../component-library'; +import { + Display, + IconColor, +} from '../../../../../helpers/constants/design-system'; +import { ConfirmInfoRow, ConfirmInfoRowProps } from './row'; + +export type ConfirmInfoExpandableRowProps = ConfirmInfoRowProps & { + content: React.ReactNode | string; + startExpanded?: boolean; +}; + +export const ConfirmInfoExpandableRow = ( + props: ConfirmInfoExpandableRowProps, +) => { + const { content, children, startExpanded, ...rowProps } = props; + + const ref = useRef() as React.MutableRefObject; + + const [expanded, setExpanded] = useState(Boolean(startExpanded)); + const [, setLoaded] = useState(false); + + const handleClick = useCallback(() => { + setExpanded(!expanded); + }, [expanded]); + + // Required to force a re-render so the content height can be calculated. + useEffect(() => { + setLoaded(true); + }, []); + + return ( + <> + + + {children} + + + + + { + // Negate the margin of the above expandable row. + // Not an issue with sequential rows due to margin collapse. + } + {content} + + + ); +}; diff --git a/ui/components/app/confirm/info/row/index.scss b/ui/components/app/confirm/info/row/index.scss new file mode 100644 index 000000000000..5595b900bf53 --- /dev/null +++ b/ui/components/app/confirm/info/row/index.scss @@ -0,0 +1,12 @@ +.expandable { + transition: height 0.3s ease-in-out; + overflow: hidden; +} + +.expandIcon { + transition: 0.3s ease-in-out; +} + +.expandIcon.expanded { + rotate: -90deg; +} diff --git a/ui/components/app/nfts-tab/nfts-tab.test.js b/ui/components/app/nfts-tab/nfts-tab.test.js index 0f661956cc30..1ef35fbb63ac 100644 --- a/ui/components/app/nfts-tab/nfts-tab.test.js +++ b/ui/components/app/nfts-tab/nfts-tab.test.js @@ -10,6 +10,11 @@ import { CHAIN_IDS, NETWORK_TYPES } from '../../../../shared/constants/network'; import { ETH_EOA_METHODS } from '../../../../shared/constants/eth-methods'; import NftsTab from '.'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: jest.fn(() => []), +})); + const ETH_BALANCE = '0x16345785d8a0000'; // 0.1 ETH const NFTS = [ @@ -229,12 +234,15 @@ describe('NFT Items', () => { }); const historyPushMock = jest.fn(); - jest - .spyOn(reactRouterDom, 'useHistory') - .mockImplementation() - .mockReturnValue({ push: historyPushMock }); + beforeEach(() => { + jest + .spyOn(reactRouterDom, 'useHistory') + .mockImplementation() + .mockReturnValue({ push: historyPushMock }); + }); afterEach(() => { + jest.resetAllMocks(); jest.clearAllMocks(); }); 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 3224d85f52a9..cfeac0b5811d 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 @@ -16,6 +16,11 @@ const middleware = [thunk]; jest.mock('../../../store/actions'); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: jest.fn(() => []), +})); + describe('SmartTransactionsOptInModal', () => { it('displays the correct text in the modal', () => { const store = configureMockStore(middleware)(createSwapsMockStore()); diff --git a/ui/components/app/wallet-overview/btc-overview.test.tsx b/ui/components/app/wallet-overview/btc-overview.test.tsx index 233096f0918c..15893fa4d204 100644 --- a/ui/components/app/wallet-overview/btc-overview.test.tsx +++ b/ui/components/app/wallet-overview/btc-overview.test.tsx @@ -9,6 +9,7 @@ import mockState from '../../../../test/data/mock-state.json'; import { renderWithProvider } from '../../../../test/jest/rendering'; import { MultichainNetworks } from '../../../../shared/constants/multichain/networks'; import { RampsMetaMaskEntry } from '../../../hooks/ramps/useRamps/useRamps'; +import { defaultBuyableChains } from '../../../ducks/ramps/constants'; import BtcOverview from './btc-overview'; const PORTOFOLIO_URL = 'https://portfolio.test'; @@ -40,43 +41,63 @@ const mockNonEvmAccount = { type: BtcAccountType.P2wpkh, }; -function getStore(state?: Record) { - return configureMockStore([thunk])({ - metamask: { - ...mockState.metamask, - internalAccounts: { - accounts: { - [mockNonEvmAccount.id]: mockNonEvmAccount, - }, - selectedAccount: mockNonEvmAccount.id, - }, - // (Multichain) BalancesController - balances: { - [mockNonEvmAccount.id]: { - [MultichainNativeAssets.BITCOIN]: { - amount: mockNonEvmBalance, - unit: 'BTC', - }, - }, - }, - // (Multichain) RatesController - fiatCurrency: 'usd', - rates: { - [Cryptocurrency.Btc]: { - conversionRate: '1.000', - conversionDate: 0, - }, +const mockBtcChain = { + active: true, + chainId: MultichainNetworks.BITCOIN, + chainName: 'Bitcoin', + shortName: 'Bitcoin', + nativeTokenSupported: true, + isEvm: false, +}; +// default chains do not include BTC +const mockBuyableChainsWithoutBtc = defaultBuyableChains.filter( + (chain) => chain.chainId !== MultichainNetworks.BITCOIN, +); +const mockBuyableChainsWithBtc = [...mockBuyableChainsWithoutBtc, mockBtcChain]; + +const mockMetamaskStore = { + ...mockState.metamask, + internalAccounts: { + accounts: { + [mockNonEvmAccount.id]: mockNonEvmAccount, + }, + selectedAccount: mockNonEvmAccount.id, + }, + // (Multichain) BalancesController + balances: { + [mockNonEvmAccount.id]: { + [MultichainNativeAssets.BITCOIN]: { + amount: mockNonEvmBalance, + unit: 'BTC', }, - cryptocurrencies: [Cryptocurrency.Btc], - // Required, during onboarding, the extension will assume we're in an "EVM context", meaning - // most multichain selectors will not use non-EVM logic despite having a non-EVM - // selected account - completedOnboarding: true, - // Used when clicking on some buttons - metaMetricsId: mockMetaMetricsId, - // Override state if provided - ...state, }, + }, + // (Multichain) RatesController + fiatCurrency: 'usd', + rates: { + [Cryptocurrency.Btc]: { + conversionRate: '1.000', + conversionDate: 0, + }, + }, + cryptocurrencies: [Cryptocurrency.Btc], + // Required, during onboarding, the extension will assume we're in an "EVM context", meaning + // most multichain selectors will not use non-EVM logic despite having a non-EVM + // selected account + completedOnboarding: true, + // Used when clicking on some buttons + metaMetricsId: mockMetaMetricsId, + // Override state if provided +}; +const mockRampsStore = { + buyableChains: mockBuyableChainsWithoutBtc, +}; + +function getStore(state?: Record) { + return configureMockStore([thunk])({ + metamask: mockMetamaskStore, + ramps: mockRampsStore, + ...state, }); } @@ -103,8 +124,11 @@ describe('BtcOverview', () => { const { container } = renderWithProvider( , getStore({ - // The balances won't be available - balances: {}, + metamask: { + ...mockMetamaskStore, + // The balances won't be available + balances: {}, + }, }), ); @@ -134,8 +158,44 @@ describe('BtcOverview', () => { expect(buyButton).toBeInTheDocument(); }); - it('opens the Portfolio "Buy & Sell" URI when clicking on "Buy & Sell" button', async () => { + it('"Buy & Sell" button is disabled if BTC is not buyable', () => { const { queryByTestId } = renderWithProvider(, getStore()); + const buyButton = queryByTestId(BTC_OVERVIEW_BUY); + + expect(buyButton).toBeInTheDocument(); + expect(buyButton).toBeDisabled(); + }); + + it('"Buy & Sell" button is enabled if BTC is buyable', () => { + const storeWithBtcBuyable = getStore({ + ramps: { + buyableChains: mockBuyableChainsWithBtc, + }, + }); + + const { queryByTestId } = renderWithProvider( + , + storeWithBtcBuyable, + ); + + const buyButton = queryByTestId(BTC_OVERVIEW_BUY); + + expect(buyButton).toBeInTheDocument(); + expect(buyButton).not.toBeDisabled(); + }); + + it('opens the Portfolio "Buy & Sell" URI when clicking on "Buy & Sell" button', async () => { + const storeWithBtcBuyable = getStore({ + ramps: { + buyableChains: mockBuyableChainsWithBtc, + }, + }); + + const { queryByTestId } = renderWithProvider( + , + storeWithBtcBuyable, + ); + const openTabSpy = jest.spyOn(global.platform, 'openTab'); const buyButton = queryByTestId(BTC_OVERVIEW_BUY); diff --git a/ui/components/app/wallet-overview/btc-overview.tsx b/ui/components/app/wallet-overview/btc-overview.tsx index 3703252f205a..f5728bf9cadd 100644 --- a/ui/components/app/wallet-overview/btc-overview.tsx +++ b/ui/components/app/wallet-overview/btc-overview.tsx @@ -1,10 +1,12 @@ import React from 'react'; - import { useSelector } from 'react-redux'; import { getMultichainProviderConfig, getMultichainSelectedAccountCachedBalance, } from '../../../selectors/multichain'; +///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) +import { getIsBitcoinBuyable } from '../../../ducks/ramps'; +///: END:ONLY_INCLUDE_IF import { CoinOverview } from './coin-overview'; type BtcOverviewProps = { @@ -14,6 +16,9 @@ type BtcOverviewProps = { const BtcOverview = ({ className }: BtcOverviewProps) => { const { chainId } = useSelector(getMultichainProviderConfig); const balance = useSelector(getMultichainSelectedAccountCachedBalance); + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) + const isBtcBuyable = useSelector(getIsBitcoinBuyable); + ///: END:ONLY_INCLUDE_IF return ( { isSwapsChain={false} ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) isBridgeChain={false} - isBuyableChain - // TODO: Remove this logic once `isNativeTokenBuyable` has been - // merged (see: https://github.com/MetaMask/metamask-extension/pull/24041) - isBuyableChainWithoutSigning + isBuyableChain={isBtcBuyable} ///: END:ONLY_INCLUDE_IF /> ); diff --git a/ui/components/app/wallet-overview/coin-buttons.tsx b/ui/components/app/wallet-overview/coin-buttons.tsx index b1774897edc4..5d710b83adfb 100644 --- a/ui/components/app/wallet-overview/coin-buttons.tsx +++ b/ui/components/app/wallet-overview/coin-buttons.tsx @@ -75,9 +75,6 @@ const CoinButtons = ({ ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) isBridgeChain, isBuyableChain, - // TODO: Remove this logic once `isNativeTokenBuyable` has been - // merged (see: https://github.com/MetaMask/metamask-extension/pull/24041) - isBuyableChainWithoutSigning = false, defaultSwapsToken, ///: END:ONLY_INCLUDE_IF classPrefix = 'coin', @@ -88,7 +85,6 @@ const CoinButtons = ({ ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) isBridgeChain: boolean; isBuyableChain: boolean; - isBuyableChainWithoutSigning?: boolean; defaultSwapsToken?: SwapsEthToken; ///: END:ONLY_INCLUDE_IF classPrefix?: string; @@ -112,10 +108,6 @@ const CoinButtons = ({ ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) { condition: !isBuyableChain, message: '' }, ///: END:ONLY_INCLUDE_IF - { - condition: !(isSigningEnabled || isBuyableChainWithoutSigning), - message: 'methodNotSupported', - }, ], sendButton: [ { condition: !isSigningEnabled, message: 'methodNotSupported' }, @@ -339,10 +331,7 @@ const CoinButtons = ({ Icon={ } - disabled={ - !isBuyableChain || - !(isSigningEnabled || isBuyableChainWithoutSigning) - } + disabled={!isBuyableChain} data-testid={`${classPrefix}-overview-buy`} label={t('buyAndSell')} onClick={handleBuyAndSellOnClick} diff --git a/ui/components/app/wallet-overview/coin-overview.tsx b/ui/components/app/wallet-overview/coin-overview.tsx index 6364b0231e82..53d98f17039d 100644 --- a/ui/components/app/wallet-overview/coin-overview.tsx +++ b/ui/components/app/wallet-overview/coin-overview.tsx @@ -39,7 +39,6 @@ export type CoinOverviewProps = { defaultSwapsToken?: SwapsEthToken; isBridgeChain: boolean; isBuyableChain: boolean; - isBuyableChainWithoutSigning: boolean; ///: END:ONLY_INCLUDE_IF isSwapsChain: boolean; isSigningEnabled: boolean; @@ -55,7 +54,6 @@ export const CoinOverview = ({ defaultSwapsToken, isBridgeChain, isBuyableChain, - isBuyableChainWithoutSigning, ///: END:ONLY_INCLUDE_IF isSwapsChain, isSigningEnabled, @@ -152,7 +150,6 @@ export const CoinOverview = ({ ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) isBridgeChain, isBuyableChain, - isBuyableChainWithoutSigning, defaultSwapsToken, ///: END:ONLY_INCLUDE_IF classPrefix, diff --git a/ui/components/app/wallet-overview/eth-overview.test.js b/ui/components/app/wallet-overview/eth-overview.test.js index 0d079a32f104..15d4e3016d2e 100644 --- a/ui/components/app/wallet-overview/eth-overview.test.js +++ b/ui/components/app/wallet-overview/eth-overview.test.js @@ -121,6 +121,7 @@ describe('EthOverview', () => { accounts: [], }, ], + balances: {}, }, ramps: { buyableChains: defaultBuyableChains, @@ -470,7 +471,6 @@ describe('EthOverview', () => { describe('Disabled buttons when an account cannot sign transactions', () => { const buttonTestCases = [ - { testId: ETH_OVERVIEW_BUY, buttonText: 'Buy & Sell' }, { testId: ETH_OVERVIEW_SEND, buttonText: 'Send' }, { testId: ETH_OVERVIEW_SWAP, buttonText: 'Swap' }, { testId: ETH_OVERVIEW_BRIDGE, buttonText: 'Bridge' }, diff --git a/ui/components/multichain/account-list-menu/account-list-menu.js b/ui/components/multichain/account-list-menu/account-list-menu.js index 8c909e72da46..6e4c7490541c 100644 --- a/ui/components/multichain/account-list-menu/account-list-menu.js +++ b/ui/components/multichain/account-list-menu/account-list-menu.js @@ -22,6 +22,9 @@ import { CreateEthAccount, ImportAccount, AccountListItemMenuTypes, + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + CreateBtcAccount, + ///: END:ONLY_INCLUDE_IF } from '..'; import { AlignItems, @@ -43,6 +46,9 @@ import { ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) getIsAddSnapAccountEnabled, ///: END:ONLY_INCLUDE_IF + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + getIsBitcoinSupportEnabled, + ///: END:ONLY_INCLUDE_IF } from '../../../selectors'; import { setSelectedAccount } from '../../../store/actions'; import { @@ -59,6 +65,9 @@ import { import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; import { getAccountLabel } from '../../../helpers/utils/accounts'; +///: BEGIN:ONLY_INCLUDE_IF(build-flask) +import { hasCreatedBtcMainnetAccount } from '../../../selectors/accounts'; +///: END:ONLY_INCLUDE_IF import { HiddenAccountList } from './hidden-account-list'; const ACTION_MODES = { @@ -68,10 +77,38 @@ const ACTION_MODES = { MENU: 'menu', // Displays the add account form controls ADD: 'add', + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + // Displays the add account form controls (for bitcoin account) + ADD_BITCOIN: 'add-bitcoin', + ///: END:ONLY_INCLUDE_IF // Displays the import account form controls IMPORT: 'import', }; +/** + * Gets the title for a given action mode. + * + * @param t - Function to translate text. + * @param actionMode - An action mode. + * @returns The title for this action mode. + */ +export const getActionTitle = (t, actionMode) => { + switch (actionMode) { + case ACTION_MODES.ADD: + return t('addAccount'); + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + case ACTION_MODES.ADD_BITCOIN: + return t('addAccount'); + ///: END:ONLY_INCLUDE_IF + case ACTION_MODES.MENU: + return t('addAccount'); + case ACTION_MODES.IMPORT: + return t('importAccount'); + default: + return t('selectAnAccount'); + } +}; + /** * Merges ordered accounts with balances with each corresponding account data from internal accounts * @@ -117,6 +154,12 @@ export const AccountListMenu = ({ ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) const addSnapAccountEnabled = useSelector(getIsAddSnapAccountEnabled); ///: END:ONLY_INCLUDE_IF + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + const bitcoinSupportEnabled = useSelector(getIsBitcoinSupportEnabled); + const isBtcMainnetAccountAlreadyCreated = useSelector( + hasCreatedBtcMainnetAccount, + ); + ///: END:ONLY_INCLUDE_IF const [searchQuery, setSearchQuery] = useState(''); const [actionMode, setActionMode] = useState(ACTION_MODES.LIST); @@ -136,12 +179,7 @@ export const AccountListMenu = ({ } searchResults = mergeAccounts(searchResults, accounts); - let title = t('selectAnAccount'); - if (actionMode === ACTION_MODES.ADD || actionMode === ACTION_MODES.MENU) { - title = t('addAccount'); - } else if (actionMode === ACTION_MODES.IMPORT) { - title = t('importAccount'); - } + const title = getActionTitle(t, actionMode); let onBack = null; if (actionMode !== ACTION_MODES.LIST) { @@ -180,6 +218,23 @@ export const AccountListMenu = ({ />
) : null} + { + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + bitcoinSupportEnabled && actionMode === ACTION_MODES.ADD_BITCOIN ? ( + + { + if (confirmed) { + onClose(); + } else { + setActionMode(ACTION_MODES.LIST); + } + }} + /> + + ) : null + ///: END:ONLY_INCLUDE_IF + } {actionMode === ACTION_MODES.IMPORT ? ( + { + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + bitcoinSupportEnabled ? ( + + { + trackEvent({ + category: MetaMetricsEventCategory.Navigation, + event: MetaMetricsEventName.AccountAddSelected, + properties: { + account_type: MetaMetricsEventAccountType.Default, + location: 'Main Menu', + }, + }); + setActionMode(ACTION_MODES.ADD_BITCOIN); + }} + data-testid="multichain-account-menu-popover-add-account" + > + {t('addNewBitcoinAccount')} + + + ) : null + ///: END:ONLY_INCLUDE_IF + } ({ })); ///: END:ONLY_INCLUDE_IF +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: jest.fn(() => []), +})); + const render = (props = { onClose: () => jest.fn() }) => { const store = configureStore({ ...mockState, @@ -70,12 +75,15 @@ const render = (props = { onClose: () => jest.fn() }) => { describe('AccountListMenu', () => { const historyPushMock = jest.fn(); - jest - .spyOn(reactRouterDom, 'useHistory') - .mockImplementation() - .mockReturnValue({ push: historyPushMock }); + beforeEach(() => { + jest + .spyOn(reactRouterDom, 'useHistory') + .mockImplementation() + .mockReturnValue({ push: historyPushMock }); + }); afterEach(() => { + jest.resetAllMocks(); jest.clearAllMocks(); }); @@ -211,7 +219,7 @@ describe('AccountListMenu', () => { // Click the button to ensure the options and close button display button[0].click(); - expect(getByText('Add a new account')).toBeInTheDocument(); + expect(getByText('Add a new Ethereum account')).toBeInTheDocument(); expect(getByText('Import account')).toBeInTheDocument(); expect(getByText('Add hardware wallet')).toBeInTheDocument(); const header = document.querySelector('header'); @@ -235,7 +243,7 @@ describe('AccountListMenu', () => { ); button.click(); - fireEvent.click(getByText('Add a new account')); + fireEvent.click(getByText('Add a new Ethereum account')); expect(getByText('Create')).toBeInTheDocument(); expect(getByText('Cancel')).toBeInTheDocument(); diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-amount.stories.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-amount.stories.tsx index c7e73f377e70..1f22ad544f0d 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-amount.stories.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-amount.stories.tsx @@ -79,6 +79,12 @@ const store = configureStore({ error: null, type: 'NATIVE', }, + receiveAsset: { + balance: '0x1158e460913d00000', + details: null, + error: null, + type: 'NATIVE', + }, fromAccount: null, gas: { error: null, diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-amount.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-amount.test.tsx index 470ca9543d46..a5215202db1c 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-amount.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-amount.test.tsx @@ -56,7 +56,11 @@ describe('AssetPickerAmount', () => { return { address: '0xAddress' }; } if (selector === getCurrentDraftTransaction) { - return { swapQuotesError: null }; + return { + swapQuotesError: null, + sendAsset: { type: AssetType.token }, + receiveAsset: { type: AssetType.token }, + }; } if (selector === getIsNativeSendPossible) { return true; @@ -84,13 +88,73 @@ describe('AssetPickerAmount', () => { expect(screen.getByText('MaxClearButton')).toBeInTheDocument(); }); + it('renders AssetPickerAmount does not show max button when srcAsset type is native and destAsset type is ERC20', () => { + useSelectorMock.mockImplementation((selector) => { + if (selector === getSelectedInternalAccount) { + return { address: '0xAddress' }; + } + if (selector === getCurrentDraftTransaction) { + return { + swapQuotesError: null, + sendAsset: { type: AssetType.native }, + receiveAsset: { type: AssetType.token }, + }; + } + if (selector === getIsNativeSendPossible) { + return true; + } + if (selector === getSendMaxModeState) { + return false; + } + return undefined; + }); + render(); + + expect(screen.getByText('AssetPicker')).toBeInTheDocument(); + expect(screen.getByText('SwappableCurrencyInput')).toBeInTheDocument(); + expect(screen.getByText('AssetBalance')).toBeInTheDocument(); + expect(screen.queryByText('MaxClearButton')).toBeNull(); + }); + + it('renders AssetPickerAmount does shows max button when srcAsset type is ERC20 and destAsset type is native', () => { + useSelectorMock.mockImplementation((selector) => { + if (selector === getSelectedInternalAccount) { + return { address: '0xAddress' }; + } + if (selector === getCurrentDraftTransaction) { + return { + swapQuotesError: null, + sendAsset: { type: AssetType.token }, + receiveAsset: { type: AssetType.native }, + }; + } + if (selector === getIsNativeSendPossible) { + return true; + } + if (selector === getSendMaxModeState) { + return false; + } + return undefined; + }); + render(); + + expect(screen.getByText('AssetPicker')).toBeInTheDocument(); + expect(screen.getByText('SwappableCurrencyInput')).toBeInTheDocument(); + expect(screen.getByText('AssetBalance')).toBeInTheDocument(); + expect(screen.getByText('MaxClearButton')).toBeInTheDocument(); + }); + it('shows swaps error message when there is an error', () => { useSelectorMock.mockImplementation((selector) => { if (selector === getSelectedInternalAccount) { return { address: '0xAddress' }; } if (selector === getCurrentDraftTransaction) { - return { swapQuotesError: 'error' }; + return { + swapQuotesError: 'error', + sendAsset: { type: AssetType.native }, + receiveAsset: { type: AssetType.token }, + }; } if (selector === getIsNativeSendPossible) { return true; @@ -118,7 +182,10 @@ describe('AssetPickerAmount', () => { return { address: '0xAddress' }; } if (selector === getCurrentDraftTransaction) { - return { swapQuotesError: null }; + return { + sendAsset: { type: AssetType.native }, + receiveAsset: { type: AssetType.token }, + }; } if (selector === getIsNativeSendPossible) { return false; @@ -140,6 +207,16 @@ describe('AssetPickerAmount', () => { onAmountChange: undefined, }; + useSelectorMock.mockImplementation((selector) => { + if (selector === getCurrentDraftTransaction) { + return { + sendAsset: { type: AssetType.native }, + receiveAsset: { type: AssetType.token }, + }; + } + return undefined; + }); + render(); expect(screen.queryByText('MaxClearButton')).not.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 80e946fe3934..190da522cad2 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-amount.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-amount.tsx @@ -62,7 +62,9 @@ export const AssetPickerAmount = ({ const selectedAccount = useSelector(getSelectedInternalAccount); const t = useI18nContext(); - const { swapQuotesError } = useSelector(getCurrentDraftTransaction); + const { swapQuotesError, sendAsset, receiveAsset } = useSelector( + getCurrentDraftTransaction, + ); const isDisabled = !onAmountChange; const isSwapsErrorShown = isDisabled && swapQuotesError; @@ -135,6 +137,10 @@ export const AssetPickerAmount = ({ borderColor = BorderColor.primaryDefault; } + const isSwapAndSendFromNative = + sendAsset.type === AssetType.native && + receiveAsset.type !== AssetType.native; + return ( )} {/* The fiat value will always leave dust and is often inaccurate anyways */} - {onAmountChange && isNativeSendPossible && ( + {onAmountChange && isNativeSendPossible && !isSwapAndSendFromNative && ( )} diff --git a/ui/components/multichain/create-btc-account/create-btc-account.stories.js b/ui/components/multichain/create-btc-account/create-btc-account.stories.js new file mode 100644 index 000000000000..71c537420eed --- /dev/null +++ b/ui/components/multichain/create-btc-account/create-btc-account.stories.js @@ -0,0 +1,10 @@ +import React from 'react'; +import { CreateBtcAccount } from '.'; + +export default { + title: 'Components/Multichain/CreateBtcAccount', + component: CreateBtcAccount, +}; + +export const DefaultStory = (args) => ; +DefaultStory.storyName = 'Default'; diff --git a/ui/components/multichain/create-btc-account/create-btc-account.test.tsx b/ui/components/multichain/create-btc-account/create-btc-account.test.tsx new file mode 100644 index 000000000000..b667e715c215 --- /dev/null +++ b/ui/components/multichain/create-btc-account/create-btc-account.test.tsx @@ -0,0 +1,100 @@ +/* eslint-disable jest/require-top-level-describe */ +import React from 'react'; +import { JsonRpcRequest } from '@metamask/utils'; +import { BtcAccountType, BtcMethod } from '@metamask/keyring-api'; +import { fireEvent, renderWithProvider, waitFor } from '../../../../test/jest'; +import configureStore from '../../../store/store'; +import mockState from '../../../../test/data/mock-state.json'; +import { MultichainNetworks } from '../../../../shared/constants/multichain/networks'; +import { CreateBtcAccount } from '.'; + +const render = (props = { onActionComplete: jest.fn() }) => { + const store = configureStore(mockState); + return renderWithProvider(, store); +}; + +const ACCOUNT_NAME = 'Bitcoin Account'; + +const mockBtcAccount = { + type: BtcAccountType.P2wpkh, + id: '8a323a0b-9ff5-4ab6-95e0-d51ec7e09763', + address: 'bc1qwl8399fz829uqvqly9tcatgrgtwp3udnhxfq4k', + options: { + scope: MultichainNetworks.BITCOIN, + index: 0, + }, + methods: [BtcMethod.SendMany], +}; +const mockBitcoinWalletSnapSend = jest.fn().mockReturnValue(mockBtcAccount); +const mockSetAccountLabel = jest.fn().mockReturnValue({ type: 'TYPE' }); + +jest.mock('../../../store/actions', () => ({ + forceUpdateMetamaskState: jest.fn(), + setAccountLabel: (address: string, label: string) => + mockSetAccountLabel(address, label), +})); + +jest.mock( + '../../../../app/scripts/lib/snap-keyring/bitcoin-wallet-snap', + () => ({ + BitcoinWalletSnapSender: jest.fn().mockImplementation(() => { + return { + send: (_request: JsonRpcRequest) => { + return mockBitcoinWalletSnapSend(); + }, + }; + }), + }), +); + +describe('CreateBtcAccount', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('displays account name input and suggests name', async () => { + const { getByPlaceholderText } = render(); + + await waitFor(() => + expect(getByPlaceholderText(ACCOUNT_NAME)).toBeInTheDocument(), + ); + }); + + it('fires onActionComplete when clicked', async () => { + const onActionComplete = jest.fn(); + const { getByText, getByPlaceholderText } = render({ onActionComplete }); + + const input = await waitFor(() => getByPlaceholderText(ACCOUNT_NAME)); + const newAccountName = 'New Account Name'; + + fireEvent.change(input, { + target: { value: newAccountName }, + }); + fireEvent.click(getByText('Create')); + + await waitFor(() => + expect(mockSetAccountLabel).toHaveBeenCalledWith( + mockBtcAccount.address, + newAccountName, + ), + ); + await waitFor(() => expect(onActionComplete).toHaveBeenCalled()); + }); + + it(`doesn't allow duplicate account names`, async () => { + const { getByText, getByPlaceholderText } = render(); + + const input = await waitFor(() => getByPlaceholderText(ACCOUNT_NAME)); + const usedAccountName = + mockState.metamask.internalAccounts.accounts[ + '07c2cfec-36c9-46c4-8115-3836d3ac9047' + ].metadata.name; + + fireEvent.change(input, { + target: { value: usedAccountName }, + }); + + const submitButton = getByText('Create'); + expect(submitButton).toHaveAttribute('disabled'); + }); +}); diff --git a/ui/components/multichain/create-btc-account/create-btc-account.tsx b/ui/components/multichain/create-btc-account/create-btc-account.tsx new file mode 100644 index 000000000000..40c8bdb169e7 --- /dev/null +++ b/ui/components/multichain/create-btc-account/create-btc-account.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { InternalAccount, KeyringClient } from '@metamask/keyring-api'; +import { useDispatch } from 'react-redux'; +import { CreateAccount } from '..'; +import { MultichainNetworks } from '../../../../shared/constants/multichain/networks'; +import { BitcoinWalletSnapSender } from '../../../../app/scripts/lib/snap-keyring/bitcoin-wallet-snap'; +import { + setAccountLabel, + forceUpdateMetamaskState, +} from '../../../store/actions'; + +type CreateBtcAccountOptions = { + /** + * Callback called once the account has been created + */ + onActionComplete: (completed: boolean) => Promise; +}; + +export const CreateBtcAccount = ({ + onActionComplete, +}: CreateBtcAccountOptions) => { + const dispatch = useDispatch(); + + const onCreateAccount = async (name: string) => { + // Trigger the Snap account creation flow + const client = new KeyringClient(new BitcoinWalletSnapSender()); + const account = await client.createAccount({ + scope: MultichainNetworks.BITCOIN, + }); + + // TODO: Use the new Snap account creation flow that also include account renaming + // For now, we just use the AccountsController to rename the account after being created + if (name) { + // READ THIS CAREFULLY: + // We have to update the redux state here, since we are updating the global state + // from the background during account creation + await forceUpdateMetamaskState(dispatch); + + // NOTE: If the renaming part of this flow fail, the account might still be created, but it + // will be named according the Snap keyring naming logic (Snap Account N). + dispatch(setAccountLabel(account.address, name)); + } + + await onActionComplete(true); + }; + + const getNextAvailableAccountName = async (_accounts: InternalAccount[]) => { + return 'Bitcoin Account'; + }; + + return ( + + ); +}; diff --git a/ui/components/multichain/create-btc-account/index.ts b/ui/components/multichain/create-btc-account/index.ts new file mode 100644 index 000000000000..99f761103277 --- /dev/null +++ b/ui/components/multichain/create-btc-account/index.ts @@ -0,0 +1 @@ +export { CreateBtcAccount } from './create-btc-account'; diff --git a/ui/components/multichain/create-eth-account/create-eth-account.stories.js b/ui/components/multichain/create-eth-account/create-eth-account.stories.js index 992fc33eda3a..255f34368cc0 100644 --- a/ui/components/multichain/create-eth-account/create-eth-account.stories.js +++ b/ui/components/multichain/create-eth-account/create-eth-account.stories.js @@ -2,7 +2,7 @@ import React from 'react'; import { CreateEthAccount } from '.'; export default { - title: 'Components/Multichain/CreateAccount', + title: 'Components/Multichain/CreateEthAccount', component: CreateEthAccount, }; diff --git a/ui/components/multichain/index.js b/ui/components/multichain/index.js index 4f4ca9468bac..2c606081c861 100644 --- a/ui/components/multichain/index.js +++ b/ui/components/multichain/index.js @@ -18,6 +18,7 @@ export { ProductTour } from './product-tour-popover'; export { AccountDetails } from './account-details'; export { CreateAccount } from './create-account'; export { CreateEthAccount } from './create-eth-account'; +export { CreateBtcAccount } from './create-btc-account'; export { ConnectedAccountsMenu } from './connected-accounts-menu'; export { ImportAccount } from './import-account'; export { ImportNftsModal } from './import-nfts-modal'; diff --git a/ui/components/multichain/pages/send/index.scss b/ui/components/multichain/pages/send/index.scss index e5bc53e100a6..9551ce8e5813 100644 --- a/ui/components/multichain/pages/send/index.scss +++ b/ui/components/multichain/pages/send/index.scss @@ -1,7 +1,11 @@ @use "design-system"; .multichain-send-page { - width: 100%; + width: 408px; + + @media (max-width: 371px) { + width: 100%; + } &__account-picker { height: 62px; diff --git a/ui/components/ui/account-list/account-list.js b/ui/components/ui/account-list/account-list.js index 77683011697a..18fc35b2c6ce 100644 --- a/ui/components/ui/account-list/account-list.js +++ b/ui/components/ui/account-list/account-list.js @@ -187,6 +187,7 @@ const AccountList = ({ { + /** nothing to do */ + }, +}; + +const account2 = { + ...createMockInternalAccount({ + address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', + name: 'Very long account name', + }), + addressLabel: 'Very long account name (0xb366c...015ba)', + lastConnectedDate: 'Feb-22-2022', + balance: '0x1bc16d674ec80000', + has: () => { + /** nothing to do */ + }, +}; + +const account3 = { + ...createMockInternalAccount({ + address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', + name: 'DeFi Account', + }), + addressLabel: 'DeFi Account (0xb366c...015ba)', + lastConnectedDate: 'Feb-22-2022', + balance: '0x2bc16d674ec80000', + has: () => { + /** nothing to do */ + }, +}; + +const account4 = { + ...createMockInternalAccount({ + address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', + name: 'Gaming Account', + }), + addressLabel: 'Gaming Account (0xb366c...015ba)', + lastConnectedDate: 'Feb-22-2022', + balance: '0x12c16d674ec80000', + has: () => { + /** nothing to do */ + }, +}; + export default { title: 'Components/UI/AccountList', // title should follow the folder structure location of the component. Don't use spaces. @@ -34,44 +87,7 @@ export default { }, }, args: { - accounts: [ - { - address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', - addressLabel: 'Account 1 (0xbc641...29713)', - lastConnectedDate: 'Feb-22-2022', - balance: '8.7a73149c048545a3fe58', - has: () => { - /** nothing to do */ - }, - }, - { - address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', - addressLabel: 'Very long account name (0xb366c...015ba)', - lastConnectedDate: 'Feb-22-2022', - balance: '0x1bc16d674ec80000', - has: () => { - /** nothing to do */ - }, - }, - { - address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', - addressLabel: 'DeFi Account (0xb366c...015ba)', - lastConnectedDate: 'Feb-22-2022', - balance: '0x2bc16d674ec80000', - has: () => { - /** nothing to do */ - }, - }, - { - address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', - addressLabel: 'Gaming Account (0xb366c...015ba)', - lastConnectedDate: 'Feb-22-2022', - balance: '0x12c16d674ec80000', - has: () => { - /** nothing to do */ - }, - }, - ], + accounts: [account1, account2, account3, account4], selectedAccounts: new Set(['0x64a845a5b02460acf8a3d84503b0d68d028b4bb4']), addressLastConnectedMap: { '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4': 'Feb-22-2022', diff --git a/ui/components/ui/account-list/account-list.test.js b/ui/components/ui/account-list/account-list.test.js index 39df0a545212..e815b11069c5 100644 --- a/ui/components/ui/account-list/account-list.test.js +++ b/ui/components/ui/account-list/account-list.test.js @@ -1,41 +1,38 @@ import React from 'react'; import { screen, fireEvent } from '@testing-library/react'; -import { - BtcAccountType, - BtcMethod, - EthAccountType, -} from '@metamask/keyring-api'; +import { BtcAccountType, BtcMethod } from '@metamask/keyring-api'; import { renderWithProvider } from '../../../../test/jest'; import configureStore from '../../../store/store'; import mockState from '../../../../test/data/mock-state.json'; -import { ETH_EOA_METHODS } from '../../../../shared/constants/eth-methods'; +import { MultichainNativeAssets } from '../../../../shared/constants/multichain/assets'; +import { createMockInternalAccount } from '../../../../test/jest/mocks'; import AccountList from './account-list'; +const ONE_ETH_IN_WEI_AS_HEX = '0xde0b6b3a7640000'; + const mockHandleAccountClick = jest.fn(); const defaultAddress = '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4'; +const defaultAccount = { + ...createMockInternalAccount({ + address: defaultAddress, + name: 'Account 1', + }), + addressLabel: 'Account 1', + balance: ONE_ETH_IN_WEI_AS_HEX, +}; +const mockNonEvmAccount = { + ...createMockInternalAccount({ + address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', + name: 'BTC Account', + type: BtcAccountType.P2wpkh, + methods: [BtcMethod.SendMany], + }), + addressLabel: 'BTC Account', + balance: '1', +}; const defaultArgs = { - accounts: [ - { - address: defaultAddress, - addressLabel: 'Account 1', - label: 'Account 1', - balance: '87a73149c048545a3fe58', - id: 'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3', - metadata: { - name: 'Account 1', - keyring: { - type: 'HD Key Tree', - }, - }, - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - has: () => { - /** nothing to do */ - }, - }, - ], + accounts: [defaultAccount], selectedAccounts: new Set([defaultAddress]), addressLastConnectedMap: { [defaultAddress]: 'Feb-22-2022', @@ -52,6 +49,31 @@ const render = (args = defaultArgs) => { const store = configureStore({ metamask: { ...mockState.metamask, + completedOnboarding: true, + internalAccounts: { + ...mockState.metamask.internalAccounts, + accounts: { + ...mockState.metamask.internalAccounts.accounts, + ...defaultArgs.accounts.reduce((acc, account) => { + acc[account.id] = account; + return acc; + }, {}), + }, + }, + accounts: { + [defaultAccount.address]: { + balance: ONE_ETH_IN_WEI_AS_HEX, + address: [defaultAccount.address], + }, + }, + balances: { + [mockNonEvmAccount.id]: { + [MultichainNativeAssets.BITCOIN]: { + amount: '1', + unit: 'BTC', + }, + }, + }, }, }); @@ -110,4 +132,19 @@ describe('AccountList', () => { fireEvent.click(accountButton); expect(mockHandleAccountClick).toHaveBeenCalled(); }); + + it('displays the correct account balance and ticker', () => { + const args = { + ...defaultArgs, + accounts: [defaultAccount, mockNonEvmAccount], + }; + process.env.DEBUG_PRINT_LIMIT = 10000000000; + const { getByText, getByTestId, getByTitle } = render(args); + expect(getByTestId('choose-account-list-0')).toBeInTheDocument(); + expect(getByTestId('choose-account-list-1')).toBeInTheDocument(); + expect(getByText(defaultAccount.addressLabel)).toBeInTheDocument(); + expect(getByText(mockNonEvmAccount.addressLabel)).toBeInTheDocument(); + expect(getByTitle('1 ETH')).toBeInTheDocument(); + expect(getByTitle('1 BTC')).toBeInTheDocument(); + }); }); diff --git a/ui/components/ui/delineator/delineator.stories.tsx b/ui/components/ui/delineator/delineator.stories.tsx new file mode 100644 index 000000000000..b2436aa931af --- /dev/null +++ b/ui/components/ui/delineator/delineator.stories.tsx @@ -0,0 +1,126 @@ +import React, { useState } from 'react'; +import { Delineator } from '.'; +import { DelineatorType } from './delineator.types'; +import { Text, IconName } from '../../component-library'; +import { + TextVariant, + TextColor, +} from '../../../helpers/constants/design-system'; + +export default { + title: 'Components/UI/Delineator', + decorators: [ + (story: () => React.ReactNode) => ( +
+ {story()} +
+ ), + ], +}; + +const headerComponent = ( + + Insights from Forta + +); +const exampleContent = ( + + This transaction will not transfer any assets from or to your account. + +); + +export const DefaultStory = () => ( + + {exampleContent} + +); + +export const InitiallyExpanded = () => ( + + {exampleContent} + +); + +export const InitiallyExpandedAndNotCollapsable = () => ( + + {exampleContent} + +); + +export const LoadingStory = () => ( + + {exampleContent} + +); + +export const ErrorStory = () => ( + + {exampleContent} + +); + +export const OnExpandChangeStory = () => { + const [expandEvents, setExpandEvents] = useState([]); + const handleOnExpandChange = (isExpanded: boolean) => { + setExpandEvents([ + ...expandEvents, + `isExpanded: ${isExpanded} at ${new Date().toLocaleTimeString()}`, + ]); + }; + + return ( + <> + + {exampleContent} + +
+ {expandEvents.map((event, index) => ( + + {event} + + ))} +
+ + ); +}; diff --git a/ui/components/ui/delineator/delineator.test.tsx b/ui/components/ui/delineator/delineator.test.tsx new file mode 100644 index 000000000000..9c100d1549c7 --- /dev/null +++ b/ui/components/ui/delineator/delineator.test.tsx @@ -0,0 +1,112 @@ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { TextColor } from '../../../helpers/constants/design-system'; +import { IconName, Text } from '../../component-library'; +import { DelineatorType } from './delineator.types'; +import { Delineator } from './delineator'; + +describe('Delineator Component', () => { + const CONTENT_TEXT_MOCK = 'Content text'; + const HEADER_TEXT_MOCK = 'Header text'; + const headerComponent = ( + Header text + ); + const exampleContent = {CONTENT_TEXT_MOCK}; + + it('renders correctly with minimal props', () => { + const { getByText } = render( + + {exampleContent} + , + ); + + expect(getByText(HEADER_TEXT_MOCK)).toBeInTheDocument(); + expect(screen.queryByText(CONTENT_TEXT_MOCK)).not.toBeInTheDocument(); + }); + + it('expands/collapses when header is clicked', () => { + const { getByText } = render( + + {exampleContent} + , + ); + + fireEvent.click(getByText(HEADER_TEXT_MOCK)); + expect(getByText(CONTENT_TEXT_MOCK)).toBeInTheDocument(); + + // Click again to collapse + fireEvent.click(getByText(HEADER_TEXT_MOCK)); + expect(screen.queryByText(CONTENT_TEXT_MOCK)).not.toBeInTheDocument(); + }); + + it('shows loading state correctly', () => { + render( + + {exampleContent} + , + ); + + expect(screen.queryByText(CONTENT_TEXT_MOCK)).not.toBeInTheDocument(); + expect(screen.getByRole('progressbar')).toBeInTheDocument(); + }); + + it('always shows content when isCollapsible is false or on collapse click', () => { + const { getByText } = render( + + {exampleContent} + , + ); + + fireEvent.click(getByText(HEADER_TEXT_MOCK)); + expect(getByText(CONTENT_TEXT_MOCK)).toBeInTheDocument(); + }); + + it('forces header text color properly when type is error', () => { + const { getByText } = render( + + {exampleContent} + , + ); + + expect(getByText(HEADER_TEXT_MOCK)).toHaveClass( + 'mm-box--color-error-default', + ); + }); + + it('calls onExpandChange when header is clicked', () => { + const onExpandChangeMock = jest.fn(); + const { getByText } = render( + + {exampleContent} + , + ); + + fireEvent.click(getByText(HEADER_TEXT_MOCK)); + expect(onExpandChangeMock).toHaveBeenCalledWith(true); + + fireEvent.click(getByText(HEADER_TEXT_MOCK)); + expect(onExpandChangeMock).toHaveBeenCalledWith(false); + }); +}); diff --git a/ui/components/ui/delineator/delineator.tsx b/ui/components/ui/delineator/delineator.tsx new file mode 100644 index 000000000000..c180d5005eaa --- /dev/null +++ b/ui/components/ui/delineator/delineator.tsx @@ -0,0 +1,164 @@ +import React, { useCallback, useState } from 'react'; +import classnames from 'classnames'; +import { + BorderRadius, + AlignItems, + BackgroundColor, + IconColor, + Display, + JustifyContent, + FlexDirection, +} from '../../../helpers/constants/design-system'; +import { + AvatarIcon, + Icon, + Box, + IconName, + IconSize, +} from '../../component-library'; +import Preloader from '../icon/preloader/preloader-icon.component'; +import { DelineatorProps, DelineatorType } from './delineator.types'; +import { getIconPropsByType, overrideTextComponentColorByType } from './utils'; + +const Loader = () => ( +
+ +
+); + +/** + * Renders the icon on the right based on the loading state. + * + * @param options - Options bag + * @param options.isExpanded - Whether the delineator is expanded. + * @returns Either a loading spinner or an arrow icon. + */ +const ExpandableIcon = ({ isExpanded }: { isExpanded: boolean }) => { + return ( + + ); +}; + +const Header = ({ + headerComponent, + iconName, + isCollapsible, + isExpanded, + isLoading, + onHeaderClick, + type, +}: { + headerComponent: DelineatorProps['headerComponent']; + iconName: IconName; + isCollapsible: boolean; + isExpanded: boolean; + isLoading: boolean; + onHeaderClick: () => void; + type?: DelineatorType; +}) => { + const iconProps = getIconPropsByType(type); + return ( + + + + {overrideTextComponentColorByType({ + component: headerComponent, + type, + })} + + {isCollapsible && + (isLoading ? : )} + + ); +}; +const Content = ({ children }: { children: React.ReactNode }) => { + return ( + + {children} + + ); +}; + +const Container = ({ + children, + wrapperBoxProps, +}: { + children: React.ReactNode; + wrapperBoxProps: DelineatorProps['wrapperBoxProps']; +}) => { + return ( + + {children} + + ); +}; + +export const Delineator: React.FC = ({ + children, + headerComponent, + iconName, + isCollapsible = true, + isExpanded: isExpandedProp, + isLoading = false, + onExpandChange, + type, + wrapperBoxProps, +}) => { + const [isExpanded, setIsExpanded] = useState(isExpandedProp || false); + const shouldShowContent = !isCollapsible || (isCollapsible && isExpanded); + + const handleHeaderClick = useCallback(() => { + if (isLoading || !isCollapsible) { + return; + } + const newExpandedState = !isExpanded; + onExpandChange?.(newExpandedState); + setIsExpanded(newExpandedState); + }, [isLoading, isCollapsible, isExpanded, onExpandChange]); + + return ( + +
+ {shouldShowContent && !isLoading && {children}} + + ); +}; diff --git a/ui/components/ui/delineator/delineator.types.ts b/ui/components/ui/delineator/delineator.types.ts new file mode 100644 index 000000000000..3f49790fae98 --- /dev/null +++ b/ui/components/ui/delineator/delineator.types.ts @@ -0,0 +1,19 @@ +import { Box, IconName, Text } from '../../component-library'; + +export type DelineatorProps = { + children?: React.ReactNode; + headerComponent: React.ReactElement; + iconName: IconName; + isCollapsible?: boolean; + isExpanded?: boolean; + isLoading?: boolean; + onExpandChange?: (isExpanded: boolean) => void; + type?: DelineatorType; + wrapperBoxProps?: React.ComponentProps; +}; + +export enum DelineatorType { + // eslint-disable-next-line @typescript-eslint/no-shadow + Error = 'error', + Default = 'default', +} diff --git a/ui/components/ui/delineator/index.scss b/ui/components/ui/delineator/index.scss new file mode 100644 index 000000000000..4d222a35b951 --- /dev/null +++ b/ui/components/ui/delineator/index.scss @@ -0,0 +1,9 @@ +.delineator { + &__header { + cursor: pointer; + + &--loading { + cursor: default; + } + } +} diff --git a/ui/components/ui/delineator/index.ts b/ui/components/ui/delineator/index.ts new file mode 100644 index 000000000000..4f11085e9589 --- /dev/null +++ b/ui/components/ui/delineator/index.ts @@ -0,0 +1 @@ +export { Delineator } from './delineator'; diff --git a/ui/components/ui/delineator/utils.ts b/ui/components/ui/delineator/utils.ts new file mode 100644 index 000000000000..5675042e8772 --- /dev/null +++ b/ui/components/ui/delineator/utils.ts @@ -0,0 +1,64 @@ +import React from 'react'; +import { + BackgroundColor, + IconColor, + TextColor, +} from '../../../helpers/constants/design-system'; +import { AvatarIconSize, IconProps, Text } from '../../component-library'; +import { DelineatorType } from './delineator.types'; + +const defaultIconProps = { + size: AvatarIconSize.Xs, +}; + +/* + * Get the icon props based on the type of delineator. + */ +export const getIconPropsByType = (type?: DelineatorType) => { + let backgroundColor; + const inverseIconColorProp = { + color: IconColor.infoInverse, + } as IconProps<'span'>; + + switch (type) { + case DelineatorType.Error: + backgroundColor = BackgroundColor.errorDefault; + break; + default: + backgroundColor = BackgroundColor.overlayAlternative; + } + return { + ...defaultIconProps, + iconProps: inverseIconColorProp, + backgroundColor, + }; +}; + +/* + * Get the text color based on the type of delineator. + */ +const getTextColorByType = (type?: DelineatorType) => { + switch (type) { + case DelineatorType.Error: + return TextColor.errorDefault; + default: + return TextColor.textAlternative; + } +}; + +/* + * Override the color of a Text component based on the type of delineator. + */ +export const overrideTextComponentColorByType = ({ + component, + type, +}: { + component: React.ReactElement; + type?: DelineatorType; +}) => { + const color = getTextColorByType(type); + + return React.cloneElement(component, { + color, + } as React.ComponentProps); +}; diff --git a/ui/components/ui/ui-components.scss b/ui/components/ui/ui-components.scss index ce19e3c8d6df..a851a6fd72d5 100644 --- a/ui/components/ui/ui-components.scss +++ b/ui/components/ui/ui-components.scss @@ -61,3 +61,4 @@ @import '../institutional/custody-labels/index'; @import '../institutional/note-to-trader/index'; @import 'form-combo-field/index'; +@import 'delineator/index'; diff --git a/ui/ducks/confirm-alerts/confirm-alerts.ts b/ui/ducks/confirm-alerts/confirm-alerts.ts index 8c587d164825..f28cb7b83acc 100644 --- a/ui/ducks/confirm-alerts/confirm-alerts.ts +++ b/ui/ducks/confirm-alerts/confirm-alerts.ts @@ -52,6 +52,11 @@ export type Alert = { * The severity of the alert. */ severity: AlertSeverity; + + /** + * URL to report issue. + */ + reportUrl?: string; }; /** diff --git a/ui/ducks/ramps/ramps.test.ts b/ui/ducks/ramps/ramps.test.ts index a9f50860cfd7..3cd543a65219 100644 --- a/ui/ducks/ramps/ramps.test.ts +++ b/ui/ducks/ramps/ramps.test.ts @@ -2,9 +2,12 @@ import { configureStore, Store } from '@reduxjs/toolkit'; import RampAPI from '../../helpers/ramps/rampApi/rampAPI'; import { getCurrentChainId, getUseExternalServices } from '../../selectors'; import { CHAIN_IDS } from '../../../shared/constants/network'; +import { getMultichainIsBitcoin } from '../../selectors/multichain'; +import { MultichainNetworks } from '../../../shared/constants/multichain/networks'; import rampsReducer, { fetchBuyableChains, getBuyableChains, + getIsBitcoinBuyable, getIsNativeTokenBuyable, } from './ramps'; import { defaultBuyableChains } from './constants'; @@ -13,11 +16,17 @@ jest.mock('../../helpers/ramps/rampApi/rampAPI'); const mockedRampAPI = RampAPI as jest.Mocked; jest.mock('../../selectors', () => ({ + ...jest.requireActual('../../selectors'), getCurrentChainId: jest.fn(), getUseExternalServices: jest.fn(), getNames: jest.fn(), })); +jest.mock('../../selectors/multichain', () => ({ + ...jest.requireActual('../../selectors/multichain'), + getMultichainIsBitcoin: jest.fn(), +})); + describe('rampsSlice', () => { let store: Store; @@ -170,6 +179,7 @@ describe('rampsSlice', () => { describe('getIsNativeTokenBuyable', () => { const getCurrentChainIdMock = jest.mocked(getCurrentChainId); + const getMultichainIsBitcoinMock = jest.mocked(getMultichainIsBitcoin); afterEach(() => { jest.restoreAllMocks(); @@ -177,30 +187,92 @@ describe('rampsSlice', () => { it('should return true when current chain is buyable', () => { getCurrentChainIdMock.mockReturnValue(CHAIN_IDS.MAINNET); + getMultichainIsBitcoinMock.mockReturnValue(false); const state = store.getState(); expect(getIsNativeTokenBuyable(state)).toEqual(true); }); it('should return false when current chain is not buyable', () => { getCurrentChainIdMock.mockReturnValue(CHAIN_IDS.GOERLI); + getMultichainIsBitcoinMock.mockReturnValue(false); + const mockBuyableChains = [{ chainId: CHAIN_IDS.MAINNET, active: true }]; + store.dispatch({ + type: 'ramps/setBuyableChains', + payload: mockBuyableChains, + }); const state = store.getState(); - expect(getIsNativeTokenBuyable(state)).toEqual(false); + expect(getIsNativeTokenBuyable(state)).toBe(false); }); - it('should return false when current chain is not a valid hex string', () => { - getCurrentChainIdMock.mockReturnValue('0x'); + it('should return true when Bitcoin is buyable and current chain is Bitcoin', () => { + getCurrentChainIdMock.mockReturnValue(MultichainNetworks.BITCOIN); + getMultichainIsBitcoinMock.mockReturnValue(true); + const mockBuyableChains = [ + { chainId: MultichainNetworks.BITCOIN, active: true }, + ]; + store.dispatch({ + type: 'ramps/setBuyableChains', + payload: mockBuyableChains, + }); const state = store.getState(); - expect(getIsNativeTokenBuyable(state)).toEqual(false); + expect(getIsNativeTokenBuyable(state)).toBe(true); + }); + + it('should return false when Bitcoin is not buyable and current chain is Bitcoin', () => { + getCurrentChainIdMock.mockReturnValue(MultichainNetworks.BITCOIN); + getMultichainIsBitcoinMock.mockReturnValue(true); + const mockBuyableChains = [ + { chainId: MultichainNetworks.BITCOIN, active: false }, + ]; + store.dispatch({ + type: 'ramps/setBuyableChains', + payload: mockBuyableChains, + }); + const state = store.getState(); + expect(getIsNativeTokenBuyable(state)).toBe(false); }); it('should return false when buyable chains is a corrupted array', () => { - const mockState = { + getCurrentChainIdMock.mockReturnValue(CHAIN_IDS.MAINNET); + getMultichainIsBitcoinMock.mockReturnValue(false); + const mockCorruptedState = { + ...store.getState(), ramps: { buyableChains: [null, null, null], }, }; - getCurrentChainIdMock.mockReturnValue(CHAIN_IDS.MAINNET); - expect(getIsNativeTokenBuyable(mockState)).toEqual(false); + expect(getIsNativeTokenBuyable(mockCorruptedState)).toBe(false); + }); + }); + + describe('getIsBitcoinBuyable', () => { + it('should return false when Bitcoin is not in buyableChains', () => { + const state = store.getState(); + expect(getIsBitcoinBuyable(state)).toBe(false); + }); + + it('should return true when Bitcoin is in buyableChains and active', () => { + const mockBuyableChains = [ + { chainId: MultichainNetworks.BITCOIN, active: true }, + ]; + store.dispatch({ + type: 'ramps/setBuyableChains', + payload: mockBuyableChains, + }); + const state = store.getState(); + expect(getIsBitcoinBuyable(state)).toBe(true); + }); + + it('should return false when Bitcoin is in buyableChains but not active', () => { + const mockBuyableChains = [ + { chainId: MultichainNetworks.BITCOIN, active: false }, + ]; + store.dispatch({ + type: 'ramps/setBuyableChains', + payload: mockBuyableChains, + }); + const state = store.getState(); + expect(getIsBitcoinBuyable(state)).toBe(false); }); }); }); diff --git a/ui/ducks/ramps/ramps.ts b/ui/ducks/ramps/ramps.ts index bd7f16573d7d..a6732996d74b 100644 --- a/ui/ducks/ramps/ramps.ts +++ b/ui/ducks/ramps/ramps.ts @@ -3,6 +3,8 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; import { getCurrentChainId, getUseExternalServices } from '../../selectors'; import RampAPI from '../../helpers/ramps/rampApi/rampAPI'; import { hexToDecimal } from '../../../shared/modules/conversion.utils'; +import { getMultichainIsBitcoin } from '../../selectors/multichain'; +import { MultichainNetworks } from '../../../shared/constants/multichain/networks'; import { defaultBuyableChains } from './constants'; import { AggregatorNetwork } from './types'; @@ -69,16 +71,34 @@ const { reducer } = rampsSlice; export const getBuyableChains = (state: any) => state.ramps?.buyableChains ?? defaultBuyableChains; +export const getIsBitcoinBuyable = createSelector( + [getBuyableChains], + (buyableChains) => + buyableChains + .filter(Boolean) + .some( + (network: AggregatorNetwork) => + network.chainId === MultichainNetworks.BITCOIN && network.active, + ), +); + export const getIsNativeTokenBuyable = createSelector( - [getCurrentChainId, getBuyableChains], - (currentChainId, buyableChains) => { + [ + getCurrentChainId, + getBuyableChains, + getIsBitcoinBuyable, + getMultichainIsBitcoin, + ], + (currentChainId, buyableChains, isBtcBuyable, isBtc) => { try { return buyableChains .filter(Boolean) - .some( - (network: AggregatorNetwork) => - String(network.chainId) === hexToDecimal(currentChainId), - ); + .some((network: AggregatorNetwork) => { + if (isBtc) { + return isBtcBuyable; + } + return String(network.chainId) === hexToDecimal(currentChainId); + }); } catch (e) { return false; } diff --git a/ui/ducks/ramps/types.ts b/ui/ducks/ramps/types.ts index 6a1571715dfe..ca6b783ec465 100644 --- a/ui/ducks/ramps/types.ts +++ b/ui/ducks/ramps/types.ts @@ -1,6 +1,8 @@ +import { CaipChainId } from '@metamask/utils'; + export type AggregatorNetwork = { active: boolean; - chainId: number; + chainId: number | CaipChainId; chainName: string; nativeTokenSupported: boolean; shortName: string; diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index 50039e02f650..8cfb7f4d5356 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -1151,7 +1151,9 @@ const slice = createSlice({ // if amount mode is MAX update amount to max of new asset, otherwise set // to zero. This will revalidate the send amount field. if (state.amountMode === AMOUNT_MODES.MAX) { - slice.caseReducers.updateAmountToMax(state); + // set amount mode back to input and change the send amount back to 0 + state.amountMode = AMOUNT_MODES.INPUT; + slice.caseReducers.updateSendAmount(state, { payload: '0x0' }); } else if (initialAssetSet === false) { if (isReceived) { draftTransaction.quotes = draftTransactionInitialState.quotes; diff --git a/ui/helpers/ramps/rampApi/rampAPI.ts b/ui/helpers/ramps/rampApi/rampAPI.ts index a1da6da8ef0c..06355d01be6e 100644 --- a/ui/helpers/ramps/rampApi/rampAPI.ts +++ b/ui/helpers/ramps/rampApi/rampAPI.ts @@ -16,7 +16,6 @@ const RampAPI = { const url = new URL('/regions/networks', rampApiBaseUrl); url.searchParams.set('context', 'extension'); const response = await fetchWithTimeout(url.toString()); - const { networks } = await response.json(); return networks; }, diff --git a/ui/helpers/utils/transactions.util.js b/ui/helpers/utils/transactions.util.js index 8c1eeb8f7db7..85ae53c578ab 100644 --- a/ui/helpers/utils/transactions.util.js +++ b/ui/helpers/utils/transactions.util.js @@ -10,7 +10,7 @@ import { import { addHexPrefix } from '../../../app/scripts/lib/util'; import { TransactionGroupStatus } from '../../../shared/constants/transaction'; import { readAddressAsContract } from '../../../shared/modules/contract-utils'; -import fetchWithCache from '../../../shared/lib/fetch-with-cache'; +import { getMethodFrom4Byte } from '../../../shared/lib/four-byte'; /** * @typedef EthersContractCall @@ -25,25 +25,6 @@ import fetchWithCache from '../../../shared/lib/fetch-with-cache'; * representation of the function. */ -async function getMethodFrom4Byte(fourBytePrefix) { - const fourByteResponse = await fetchWithCache({ - url: `https://www.4byte.directory/api/v1/signatures/?hex_signature=${fourBytePrefix}`, - fetchOptions: { - referrerPolicy: 'no-referrer-when-downgrade', - body: null, - method: 'GET', - mode: 'cors', - }, - functionName: 'getMethodFrom4Byte', - }); - fourByteResponse.results.sort((a, b) => { - return new Date(a.created_at).getTime() < new Date(b.created_at).getTime() - ? -1 - : 1; - }); - return fourByteResponse.results[0].text_signature; -} - let registry; /** diff --git a/ui/hooks/useCopyToClipboard.js b/ui/hooks/useCopyToClipboard.js index d658189c726b..f5573f33d1be 100644 --- a/ui/hooks/useCopyToClipboard.js +++ b/ui/hooks/useCopyToClipboard.js @@ -12,6 +12,10 @@ import { useTimeout } from './useTimeout'; */ const DEFAULT_DELAY = MINUTE; +/** + * @param delay - delay in ms + * @returns {[boolean, (text: string) => void]} + */ export function useCopyToClipboard(delay = DEFAULT_DELAY) { const [copied, setCopied] = useState(false); const startTimeout = useTimeout( diff --git a/ui/index.js b/ui/index.js index bf3ee97f57a9..0ee95c12d2d1 100644 --- a/ui/index.js +++ b/ui/index.js @@ -79,7 +79,19 @@ export default function launchMetamaskUi(opts, cb) { }); } -async function startApp(metamaskState, backgroundConnection, opts) { +/** + * Method to setup initial redux store for the ui application + * + * @param {*} metamaskState - flatten background state + * @param {*} backgroundConnection - rpc client connecting to the background process + * @param {*} activeTab - active browser tab + * @returns redux store + */ +export async function setupInitialStore( + metamaskState, + backgroundConnection, + activeTab, +) { // parse opts if (!metamaskState.featureFlags) { metamaskState.featureFlags = {}; @@ -94,7 +106,7 @@ async function startApp(metamaskState, backgroundConnection, opts) { } const draftInitialState = { - activeTab: opts.activeTab, + activeTab, // metamaskState represents the cross-tab state metamask: metamaskState, @@ -161,6 +173,16 @@ async function startApp(metamaskState, backgroundConnection, opts) { ); } + return store; +} + +async function startApp(metamaskState, backgroundConnection, opts) { + const store = await setupInitialStore( + metamaskState, + backgroundConnection, + opts.activeTab, + ); + // global metamask api - used by tooling global.metamask = { updateCurrentLocale: (code) => { diff --git a/ui/pages/confirmations/components/confirm/header/__snapshots__/header-info.test.tsx.snap b/ui/pages/confirmations/components/confirm/header/__snapshots__/header-info.test.tsx.snap index 283875972a0a..c8ba8db37118 100644 --- a/ui/pages/confirmations/components/confirm/header/__snapshots__/header-info.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/header/__snapshots__/header-info.test.tsx.snap @@ -18,7 +18,7 @@ exports[`Header should match snapshot 1`] = ` -
+
+
renders component for advanced transaction details

- Hex + Data

-

- 0xd0e30db0 -

-
-
-
-
- + + + + + + +
diff --git a/ui/pages/confirmations/components/confirm/info/shared/advanced-details/advanced-details.tsx b/ui/pages/confirmations/components/confirm/info/shared/advanced-details/advanced-details.tsx index 853848aa994e..53eda5c9c9ad 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/advanced-details/advanced-details.tsx +++ b/ui/pages/confirmations/components/confirm/info/shared/advanced-details/advanced-details.tsx @@ -1,23 +1,11 @@ -import { TransactionMeta } from '@metamask/transaction-controller'; import React, { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { ConfirmInfoRow, - ConfirmInfoRowDivider, ConfirmInfoRowText, } from '../../../../../../../components/app/confirm/info/row'; -import { - Box, - Button, - ButtonSize, - ButtonVariant, - IconName, -} from '../../../../../../../components/component-library'; -import Tooltip from '../../../../../../../components/ui/tooltip'; -import { useCopyToClipboard } from '../../../../../../../hooks/useCopyToClipboard'; import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; import { - currentConfirmationSelector, getCustomNonceValue, getNextSuggestedNonce, getUseNonceField, @@ -27,18 +15,8 @@ import { showModal, updateCustomNonce, } from '../../../../../../../store/actions'; -import { useKnownMethodDataInTransaction } from '../../hooks/known-method-data-in-transaction'; import { ConfirmInfoSection } from '../../../../../../../components/app/confirm/info/row/section'; - -const getMethodDataString = (rawMethodData: { - name: string; - params: { type: string }[]; -}): string => { - const paramsStr = rawMethodData.params.map((param) => param.type).join(','); - const result = `FUNCTION TYPE: ${rawMethodData.name} (${paramsStr})`; - - return result; -}; +import { TransactionData } from '../transaction-data/transaction-data'; const NonceDetails = () => { const t = useI18nContext(); @@ -68,90 +46,28 @@ const NonceDetails = () => { const displayedNonce = customNonceValue || nextNonce; return ( - - openEditNonceModal() : undefined} - editIconClassName="edit-nonce-btn" - /> - - ); -}; - -const DataDetails = () => { - const t = useI18nContext(); - - const currentConfirmation = useSelector( - currentConfirmationSelector, - ) as TransactionMeta; - - const { knownMethodData } = - useKnownMethodDataInTransaction(currentConfirmation); - - if (!knownMethodData?.name) { - return null; - } - - const methodDataString = getMethodDataString(knownMethodData); - - return ( - <> - - - - - - ); -}; - -const HexDetails = () => { - const t = useI18nContext(); - - const currentConfirmation = useSelector( - currentConfirmationSelector, - ) as TransactionMeta; - - const [copied, handleCopy] = useCopyToClipboard(); - - if (!currentConfirmation?.txParams?.data) { - return null; - } - - return ( - <> - - - + + + openEditNonceModal() : undefined + } + editIconClassName="edit-nonce-btn" + /> - - - - - - + ); }; export const AdvancedDetails: React.FC = () => { return ( - + <> - - - + + ); }; diff --git a/ui/pages/confirmations/components/confirm/info/shared/transaction-data/__snapshots__/transaction-data.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/shared/transaction-data/__snapshots__/transaction-data.test.tsx.snap new file mode 100644 index 000000000000..507bea3934eb --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/shared/transaction-data/__snapshots__/transaction-data.test.tsx.snap @@ -0,0 +1,2093 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TransactionData renders decoded data with names and descriptions 1`] = ` +
+
+
+
+

+ Data +

+
+
+
+
+
+

+ Function +

+
+
+
+

+ WRAP_ETH +

+
+ +
+
+