diff --git a/.circleci/scripts/git-diff-default-branch.ts b/.circleci/scripts/git-diff-default-branch.ts index 6128b80eecca..62b11e83d3fd 100644 --- a/.circleci/scripts/git-diff-default-branch.ts +++ b/.circleci/scripts/git-diff-default-branch.ts @@ -105,10 +105,12 @@ async function gitDiff(): Promise { return diffResult; } -function writePrBodyToFile(prBody: string) { +function writePrBodyAndInfoToFile(prInfo: PRInfo) { const prBodyPath = path.resolve(CHANGED_FILES_DIR, 'pr-body.txt'); - fs.writeFileSync(prBodyPath, prBody.trim()); - console.log(`PR body saved to ${prBodyPath}`); + const labels = prInfo.labels.map(label => label.name).join(', '); + const updatedPrBody = `PR labels: {${labels}}\nPR base: {${prInfo.base.ref}}\n${prInfo.body.trim()}`; + fs.writeFileSync(prBodyPath, updatedPrBody); + console.log(`PR body and info saved to ${prBodyPath}`); } /** @@ -135,17 +137,9 @@ async function storeGitDiffOutputAndPrBody() { if (!baseRef) { console.log('Not a PR, skipping git diff'); return; - } else if (baseRef !== GITHUB_DEFAULT_BRANCH) { - console.log(`This is for a PR targeting '${baseRef}', skipping git diff`); - writePrBodyToFile(prInfo.body); - return; - } else if ( - prInfo.labels.some((label) => label.name === 'skip-e2e-quality-gate') - ) { - console.log('PR has the skip-e2e-quality-gate label, skipping git diff'); - return; } - + // We perform git diff even if the PR base is not main or skip-e2e-quality-gate label is applied + // because we rely on the git diff results for other jobs console.log('Attempting to get git diff...'); const diffOutput = await gitDiff(); console.log(diffOutput); @@ -155,7 +149,7 @@ async function storeGitDiffOutputAndPrBody() { fs.writeFileSync(outputPath, diffOutput.trim()); console.log(`Git diff results saved to ${outputPath}`); - writePrBodyToFile(prInfo.body); + writePrBodyAndInfoToFile(prInfo); process.exit(0); } catch (error: any) { diff --git a/.eslintrc.js b/.eslintrc.js index a8ed5a7b6d8c..c8e449a10cbb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -299,14 +299,8 @@ module.exports = { rules: { 'react/no-unused-prop-types': 'warn', 'react/no-unused-state': 'warn', - 'react/jsx-boolean-value': 'warn', - 'react/jsx-curly-brace-presence': [ - 'warn', - { - props: 'never', - children: 'never', - }, - ], + 'react/jsx-boolean-value': 'off', + 'react/jsx-curly-brace-presence': 'off', 'react/no-deprecated': 'warn', 'react/default-props-match-prop-types': 'warn', 'react/jsx-no-duplicate-props': 'warn', diff --git a/.github/scripts/add-release-label-to-pr-and-linked-issues.ts b/.github/scripts/add-release-label-to-pr-and-linked-issues.ts index a9b84532c8e6..1c4d551828c3 100644 --- a/.github/scripts/add-release-label-to-pr-and-linked-issues.ts +++ b/.github/scripts/add-release-label-to-pr-and-linked-issues.ts @@ -6,6 +6,7 @@ import { retrieveLinkedIssues } from './shared/issue'; import { Label } from './shared/label'; import { Labelable, addLabelToLabelable } from './shared/labelable'; import { retrievePullRequest } from './shared/pull-request'; +import { isValidVersionFormat } from './shared/utils'; main().catch((error: Error): void => { console.error(error); @@ -90,9 +91,3 @@ async function main(): Promise { await addLabelToLabelable(octokit, linkedIssue, releaseLabel); } } - -// This helper function checks if version has the correct format: "x.y.z" where "x", "y" and "z" are numbers. -function isValidVersionFormat(str: string): boolean { - const regex = /^\d+\.\d+\.\d+$/; - return regex.test(str); -} diff --git a/.github/scripts/check-template-and-add-labels.ts b/.github/scripts/check-template-and-add-labels.ts index 1a2bc0410beb..dd54fe7e849e 100644 --- a/.github/scripts/check-template-and-add-labels.ts +++ b/.github/scripts/check-template-and-add-labels.ts @@ -13,6 +13,8 @@ import { } from './shared/labelable'; import { Label, + RegressionStage, + craftRegressionLabel, externalContributorLabel, flakyTestsLabel, invalidIssueTemplateLabel, @@ -21,14 +23,6 @@ import { import { TemplateType, templates } from './shared/template'; import { retrievePullRequest } from './shared/pull-request'; -enum RegressionStage { - DevelopmentFeature, - DevelopmentMain, - Testing, - Beta, - Production, -} - const knownBots = [ 'metamaskbot', 'dependabot', @@ -345,59 +339,3 @@ async function userBelongsToMetaMaskOrg( return Boolean(userBelongsToMetaMaskOrgResult?.user?.organization?.id); } - -// This function crafts appropriate label, corresponding to regression stage and release version. -function craftRegressionLabel( - regressionStage: RegressionStage | undefined, - releaseVersion: string | undefined, -): Label { - switch (regressionStage) { - case RegressionStage.DevelopmentFeature: - return { - name: `feature-branch-bug`, - color: '5319E7', // violet - description: `bug that was found on a feature branch, but not yet merged in main branch`, - }; - - case RegressionStage.DevelopmentMain: - return { - name: `regression-main`, - color: '5319E7', // violet - description: `Regression bug that was found on main branch, but not yet present in production`, - }; - - case RegressionStage.Testing: - return { - name: `regression-RC-${releaseVersion || '*'}`, - color: '744C11', // orange - description: releaseVersion - ? `Regression bug that was found in release candidate (RC) for release ${releaseVersion}` - : `TODO: Unknown release version. Please replace with correct 'regression-RC-x.y.z' label, where 'x.y.z' is the number of the release where bug was found.`, - }; - - case RegressionStage.Beta: - return { - name: `regression-beta-${releaseVersion || '*'}`, - color: 'D94A83', // pink - description: releaseVersion - ? `Regression bug that was found in beta in release ${releaseVersion}` - : `TODO: Unknown release version. Please replace with correct 'regression-beta-x.y.z' label, where 'x.y.z' is the number of the release where bug was found.`, - }; - - case RegressionStage.Production: - return { - name: `regression-prod-${releaseVersion || '*'}`, - color: '5319E7', // violet - description: releaseVersion - ? `Regression bug that was found in production in release ${releaseVersion}` - : `TODO: Unknown release version. Please replace with correct 'regression-prod-x.y.z' label, where 'x.y.z' is the number of the release where bug was found.`, - }; - - default: - return { - name: `regression-*`, - color: 'EDEDED', // grey - description: `TODO: Unknown regression stage. Please replace with correct regression label: 'regression-main', 'regression-RC-x.y.z', or 'regression-prod-x.y.z' label, where 'x.y.z' is the number of the release where bug was found.`, - }; - } -} diff --git a/.github/scripts/create-bug-report-issue.ts b/.github/scripts/create-bug-report-issue.ts new file mode 100644 index 000000000000..cfd2d7da93ae --- /dev/null +++ b/.github/scripts/create-bug-report-issue.ts @@ -0,0 +1,129 @@ +import * as core from '@actions/core'; +import { context, getOctokit } from '@actions/github'; +import { GitHub } from '@actions/github/lib/utils'; + +import { createIssue, retrieveIssueByTitle } from './shared/issue'; +import { + Label, + RegressionStage, + craftRegressionLabel, + craftTeamLabel, + createOrRetrieveLabel, + typeBugLabel, +} from './shared/label'; +import { codeRepoToPlanningRepo, codeRepoToPlatform, getCurrentDateFormatted, isValidVersionFormat } from './shared/utils'; +import { addIssueToGithubProject, GithubProject, GithubProjectField, retrieveGithubProject, updateGithubProjectDateFieldValue } from './shared/project'; + +main().catch((error: Error): void => { + console.error(error); + process.exit(1); +}); + +async function main(): Promise { + // "GITHUB_TOKEN" is an automatically generated, repository-specific access token provided by GitHub Actions. + // We can't use "GITHUB_TOKEN" here, as its permissions don't allow neither to create new labels + // nor to retrieve the content of organisations Github Projects. + // In our case, we may want to create "regression-RC-x.y.z" label when it doesn't already exist. + // We may also want to retrieve the content of organisation's Github Projects. + // As a consequence, we need to create our own "BUG_REPORT_TOKEN" with "repo" and "read:org" permissions. + // Such a token allows both to create new labels and fetch the content of organisation's Github Projects. + const personalAccessToken = process.env.BUG_REPORT_TOKEN; + if (!personalAccessToken) { + core.setFailed('BUG_REPORT_TOKEN not found'); + process.exit(1); + } + + const projectNumber = Number(process.env.RELEASES_GITHUB_PROJECT_BOARD_NUMBER); + if (!projectNumber) { + core.setFailed('RELEASES_GITHUB_PROJECT_BOARD_NUMBER not found'); + process.exit(1); + } + + const projectViewNumber = Number(process.env.RELEASES_GITHUB_PROJECT_BOARD_VIEW_NUMBER); + if (!projectViewNumber) { + core.setFailed('RELEASES_GITHUB_PROJECT_BOARD_VIEW_NUMBER not found'); + process.exit(1); + } + + const releaseVersion = process.env.RELEASE_VERSION; + if (!releaseVersion) { + core.setFailed('RELEASE_VERSION not found'); + process.exit(1); + } + if (!isValidVersionFormat(releaseVersion)) { + core.setFailed(`Invalid format for RELEASE_VERSION: ${releaseVersion}. Expected format: x.y.z`); + process.exit(1); + } + + const repoOwner = context.repo.owner; + if (!repoOwner) { + core.setFailed('repo owner not found'); + process.exit(1); + } + const codeRepoName = context.repo.repo; + if (!codeRepoName) { + core.setFailed('code repo name not found'); + process.exit(1); + } + const planningRepoName = codeRepoToPlanningRepo[codeRepoName]; + if (!planningRepoName) { + core.setFailed('planning repo name not found'); + process.exit(1); + } + + // Retrieve platform name + const platformName = codeRepoToPlatform[codeRepoName]; + if (!platformName) { + core.setFailed('platform name not found'); + process.exit(1); + } + + // Initialise octokit, required to call Github GraphQL API + const octokit: InstanceType = getOctokit(personalAccessToken, { + previews: ['bane'], // The "bane" preview is required for adding, updating, creating and deleting labels. + }); + + // Craft regression labels to add + const regressionLabelTesting: Label = craftRegressionLabel(RegressionStage.Testing, releaseVersion); + const regressionLabelProduction: Label = craftRegressionLabel(RegressionStage.Production, releaseVersion); + const teamLabel: Label = craftTeamLabel(`${platformName}-platform`); + + // Create or retrieve the different labels + await createOrRetrieveLabel(octokit, repoOwner, codeRepoName, regressionLabelProduction); + await createOrRetrieveLabel(octokit, repoOwner, codeRepoName, regressionLabelTesting); + await createOrRetrieveLabel(octokit, repoOwner, planningRepoName, regressionLabelProduction); + const regressionLabelTestingId = await createOrRetrieveLabel(octokit, repoOwner, planningRepoName, regressionLabelTesting); + const typeBugLabelId = await createOrRetrieveLabel(octokit, repoOwner, planningRepoName, typeBugLabel); + const teamLabelId = await createOrRetrieveLabel(octokit, repoOwner, planningRepoName, teamLabel); + + const issueTitle = `v${releaseVersion} Bug Report`; + const issueWithSameTitle = await retrieveIssueByTitle(octokit, repoOwner, planningRepoName, issueTitle); + if (issueWithSameTitle) { + core.setFailed(`Bug report already exists: https://github.com/${repoOwner}/${planningRepoName}/issues/${issueWithSameTitle.number}. This is not desired, but can happen in cases where a release gets re-cut.`); + process.exit(1); + } + + const issueBody = `**What is this bug report issue for?**\n\n1. This issue is used to track release dates on this [Github Project board](https://github.com/orgs/MetaMask/projects/${projectNumber}/views/${projectViewNumber}), which content then gets pulled into our metrics system.\n\n2. This issue is also used by our Zapier automations, to determine if automated notifications shall be sent on Slack for release \`${releaseVersion}\`. Notifications will only be sent as long as this issue is open.\n\n**Who created and/or closed this issue?**\n\n- This issue was automatically created by a GitHub action upon the creation of the release branch \`Version-v${releaseVersion}\`, indicating the release was cut.\n\n- This issue gets automatically closed by another GitHub action, once the \`Version-v${releaseVersion}\` branch merges into \`main\`, indicating the release is prepared for store submission.`; + const issueId = await createIssue(octokit, repoOwner, planningRepoName, issueTitle, issueBody, [regressionLabelTestingId, typeBugLabelId, teamLabelId]); + + // Retrieve project, in order to obtain its ID + const project: GithubProject = await retrieveGithubProject(octokit, projectNumber); + + const projectFieldName: string = "RC Cut"; + + const projectField: GithubProjectField | undefined = project.fields.find(field => field.name === projectFieldName); + + if (!projectField) { + throw new Error(`Project field with name ${projectFieldName} was not found on Github Project with ID ${project.id}.`); + } + + if (!projectField.id) { + throw new Error(`Project field with name ${projectFieldName} was found on Github Project with ID ${project.id}, but it has no 'id' property.`); + } + + // Add bug report issue to 'Releases' Github Project Board + await addIssueToGithubProject(octokit, project.id, issueId); + + // Update bug report issue's date property on 'Releases' Github Project Board + await updateGithubProjectDateFieldValue(octokit, project.id, projectField.id, issueId, getCurrentDateFormatted()); +} diff --git a/.github/scripts/shared/issue.ts b/.github/scripts/shared/issue.ts index e5c6804630ed..809667a24b8e 100644 --- a/.github/scripts/shared/issue.ts +++ b/.github/scripts/shared/issue.ts @@ -1,6 +1,30 @@ import { GitHub } from '@actions/github/lib/utils'; import { LabelableType, Labelable } from './labelable'; +import { retrieveRepo } from './repo'; + +interface RawIssue { + id: string; + title: string; + number: number; + createdAt: string; + body: string; + author: { + login: string; + }; + labels: { + nodes: { + id: string; + name: string; + }[]; + }; + repository: { + name: string; + owner: { + login: string; + }; + }; +} // This function retrieves an issue on a specific repo export async function retrieveIssue( @@ -14,6 +38,8 @@ export async function retrieveIssue( repository(owner: $repoOwner, name: $repoName) { issue(number: $issueNumber) { id + title + number createdAt body author { @@ -25,6 +51,12 @@ export async function retrieveIssue( name } } + repository { + name + owner { + login + } + } } } } @@ -32,20 +64,7 @@ export async function retrieveIssue( const retrieveIssueResult: { repository: { - issue: { - id: string; - createdAt: string; - body: string; - author: { - login: string; - }; - labels: { - nodes: { - id: string; - name: string; - }[]; - }; - }; + issue: RawIssue; }; } = await octokit.graphql(retrieveIssueQuery, { repoOwner, @@ -68,6 +87,78 @@ export async function retrieveIssue( return issue; } +// This function retrieves an issue by title on a specific repo +export async function retrieveIssueByTitle( + octokit: InstanceType, + repoOwner: string, + repoName: string, + issueTitle: string, +): Promise { + const searchQuery = `repo:${repoOwner}/${repoName} type:issue in:title ${issueTitle}`; + + const retrieveIssueByTitleQuery = ` + query GetIssueByTitle($searchQuery: String!) { + search( + query: $searchQuery + type: ISSUE + first: 10 + ) { + nodes { + ... on Issue { + id + title + number + createdAt + body + author { + login + } + labels(first: 100) { + nodes { + id + name + } + } + repository { + name + owner { + login + } + } + } + } + issueCount + } + } + `; + + const retrieveIssueByTitleResult: { + search: { + nodes: RawIssue[]; + }; + } = await octokit.graphql(retrieveIssueByTitleQuery, { + searchQuery, + }); + + const issueWithSameTitle = retrieveIssueByTitleResult?.search?.nodes?.find(rawIssue => rawIssue.title === issueTitle); + + const issue: Labelable | undefined = issueWithSameTitle + ? { + id: issueWithSameTitle?.id, + type: LabelableType.Issue, + number: issueWithSameTitle?.number, + repoOwner: repoOwner, + repoName: repoName, + createdAt: issueWithSameTitle?.createdAt, + body: issueWithSameTitle?.body, + author: issueWithSameTitle?.author?.login, + labels: issueWithSameTitle?.labels?.nodes, + } + : undefined; + + return issue; +} + // This function retrieves the list of linked issues for a pull request export async function retrieveLinkedIssues( octokit: InstanceType, @@ -75,6 +166,7 @@ export async function retrieveLinkedIssues( repoName: string, prNumber: number, ): Promise { + // We assume there won't be more than 100 linked issues const retrieveLinkedIssuesQuery = ` query ($repoOwner: String!, $repoName: String!, $prNumber: Int!) { @@ -83,6 +175,7 @@ export async function retrieveLinkedIssues( closingIssuesReferences(first: 100) { nodes { id + title number createdAt body @@ -112,27 +205,7 @@ export async function retrieveLinkedIssues( repository: { pullRequest: { closingIssuesReferences: { - nodes: Array<{ - id: string; - number: number; - createdAt: string; - body: string; - author: { - login: string; - }; - labels: { - nodes: { - id: string; - name: string; - }[]; - }; - repository: { - name: string; - owner: { - login: string; - }; - }; - }>; + nodes: RawIssue[]; }; }; }; @@ -144,27 +217,7 @@ export async function retrieveLinkedIssues( const linkedIssues: Labelable[] = retrieveLinkedIssuesResult?.repository?.pullRequest?.closingIssuesReferences?.nodes?.map( - (issue: { - id: string; - number: number; - createdAt: string; - body: string; - author: { - login: string; - }; - labels: { - nodes: { - id: string; - name: string; - }[]; - }; - repository: { - name: string; - owner: { - login: string; - }; - }; - }) => { + (issue: RawIssue) => { return { id: issue?.id, type: LabelableType.Issue, @@ -181,3 +234,60 @@ export async function retrieveLinkedIssues( return linkedIssues; } + +// This function creates an issue on a specific repo +export async function createIssue( + octokit: InstanceType, + repoOwner: string, + repoName: string, + issueTitle: string, + issueBody: string, + labelIds: string[], +): Promise { + // Retrieve PR's repo + const repoId = await retrieveRepo(octokit, repoOwner, repoName); + + const createIssueMutation = ` + mutation CreateIssue($repoId: ID!, $issueTitle: String!, $issueBody: String!, $labelIds: [ID!]) { + createIssue(input: {repositoryId: $repoId, title: $issueTitle, body: $issueBody, labelIds: $labelIds}) { + issue { + id + title + number + createdAt + body + author { + login + } + labels(first: 100) { + nodes { + id + name + } + } + repository { + name + owner { + login + } + } + } + } + } + `; + + const createIssueResult: { + createIssue: { + issue: RawIssue; + }; + } = await octokit.graphql(createIssueMutation, { + repoId, + issueTitle, + issueBody, + labelIds, + }); + + const issueId = createIssueResult?.createIssue?.issue?.id; + + return issueId; +} diff --git a/.github/scripts/shared/label.ts b/.github/scripts/shared/label.ts index d218dcf42570..42447e27937f 100644 --- a/.github/scripts/shared/label.ts +++ b/.github/scripts/shared/label.ts @@ -2,12 +2,25 @@ import { GitHub } from '@actions/github/lib/utils'; import { retrieveRepo } from './repo'; +export enum RegressionStage { + DevelopmentFeature, + DevelopmentMain, + Testing, + Beta, + Production, +} export interface Label { name: string; color: string; description: string; } +export const typeBugLabel: Label = { + name: 'type-bug', + color: 'D73A4A', + description: `Something isn't working`, +}; + export const externalContributorLabel: Label = { name: 'external-contributor', color: '7057FF', @@ -32,6 +45,88 @@ export const invalidPullRequestTemplateLabel: Label = { description: "PR's body doesn't match template", }; +// This function crafts appropriate label, corresponding to regression stage and release version. +export function craftRegressionLabel( + regressionStage: RegressionStage | undefined, + releaseVersion: string | undefined, +): Label { + switch (regressionStage) { + case RegressionStage.DevelopmentFeature: + return { + name: `feature-branch-bug`, + color: '5319E7', // violet + description: `bug that was found on a feature branch, but not yet merged in main branch`, + }; + + case RegressionStage.DevelopmentMain: + return { + name: `regression-main`, + color: '5319E7', // violet + description: `Regression bug that was found on main branch, but not yet present in production`, + }; + + case RegressionStage.Testing: + return { + name: `regression-RC-${releaseVersion || '*'}`, + color: '744C11', // orange + description: releaseVersion + ? `Regression bug that was found in release candidate (RC) for release ${releaseVersion}` + : `TODO: Unknown release version. Please replace with correct 'regression-RC-x.y.z' label, where 'x.y.z' is the number of the release where bug was found.`, + }; + + case RegressionStage.Beta: + return { + name: `regression-beta-${releaseVersion || '*'}`, + color: 'D94A83', // pink + description: releaseVersion + ? `Regression bug that was found in beta in release ${releaseVersion}` + : `TODO: Unknown release version. Please replace with correct 'regression-beta-x.y.z' label, where 'x.y.z' is the number of the release where bug was found.`, + }; + + case RegressionStage.Production: + return { + name: `regression-prod-${releaseVersion || '*'}`, + color: '5319E7', // violet + description: releaseVersion + ? `Regression bug that was found in production in release ${releaseVersion}` + : `TODO: Unknown release version. Please replace with correct 'regression-prod-x.y.z' label, where 'x.y.z' is the number of the release where bug was found.`, + }; + + default: + return { + name: `regression-*`, + color: 'EDEDED', // grey + description: `TODO: Unknown regression stage. Please replace with correct regression label: 'regression-main', 'regression-RC-x.y.z', or 'regression-prod-x.y.z' label, where 'x.y.z' is the number of the release where bug was found.`, + }; + } +} + +// This function crafts appropriate label, corresponding to team name. +export function craftTeamLabel(teamName: string): Label { + switch (teamName) { + case 'extension-platform': + return { + name: `team-${teamName}`, + color: '#BFD4F2', // light blue + description: `Extension Platform team`, + }; + + case 'mobile-platform': + return { + name: `team-${teamName}`, + color: '#76E9D0', // light green + description: `Mobile Platform team`, + }; + + default: + return { + name: `team-*`, + color: 'EDEDED', // grey + description: `TODO: Unknown team. Please replace with correct team label.`, + }; + } +} + // This function creates or retrieves the label on a specific repo export async function createOrRetrieveLabel( octokit: InstanceType, diff --git a/.github/scripts/shared/project.ts b/.github/scripts/shared/project.ts new file mode 100644 index 000000000000..5dc672107d16 --- /dev/null +++ b/.github/scripts/shared/project.ts @@ -0,0 +1,270 @@ +import { GitHub } from '@actions/github/lib/utils'; +import { isValidDateFormat } from './utils'; + +const MAX_NB_FETCHES = 10; // For protection against infinite loops. + +export interface GithubProject { + id: string; + fields: GithubProjectField[]; +} + +export interface GithubProjectField { + id: string; + name: string; +} + +export interface GithubProjectIssueFieldValues { + id: string; // ID of the issue (unrelated to the Github Project board) + itemId: string; // ID of the issue, as an item of the Github Project board + cutDate: string; // "RC cut date" field value of the issue, as an item of the Github Project board +} + +interface RawGithubProjectIssueFieldValues { + id: string; + content: { + id: string; + }; + cutDate: { + date: string; + }; +} + +interface RawGithubProjectIssuesFieldValues { + pageInfo: { + endCursor: string; + }; + nodes: RawGithubProjectIssueFieldValues[]; +} + +// This function retrieves a Github Project +export async function retrieveGithubProject( + octokit: InstanceType, + projectNumber: number, +): Promise { + const retrieveProjectQuery = ` + query ($projectNumber: Int!) { + organization(login: "MetaMask") { + projectV2(number: $projectNumber) { + id + fields(first: 20) { + nodes { + ... on ProjectV2Field { + id + name + } + } + } + } + } + } + `; + + const retrieveProjectResult: { + organization: { + projectV2: { + id: string; + fields: { + nodes: { + id: string; + name: string; + }[]; + }; + }; + }; + } = await octokit.graphql(retrieveProjectQuery, { + projectNumber, + }); + + const project: GithubProject = { + id: retrieveProjectResult?.organization?.projectV2?.id, + fields: retrieveProjectResult?.organization.projectV2?.fields?.nodes, + }; + + if (!project) { + throw new Error(`Project with number ${projectNumber} was not found.`); + } + + if (!project.id) { + throw new Error(`Project with number ${projectNumber} was found, but it has no 'id' property.`); + } + + if (!project.fields) { + throw new Error(`Project with number ${projectNumber} was found, but it has no 'fields' property.`); + } + + return project; +} + +// This function retrieves a Github Project's issues' field values +export async function retrieveGithubProjectIssuesFieldValues( + octokit: InstanceType, + projectId: string, + cursor: string | undefined, +): Promise { + const after = cursor ? `after: "${cursor}"` : ''; + + const retrieveProjectIssuesFieldValuesQuery = ` + query ($projectId: ID!) { + node(id: $projectId) { + ... on ProjectV2 { + items( + first: 100 + ${after} + ) { + pageInfo { + endCursor + } + nodes { + id + content { + ... on Issue { + id + } + } + cutDate: fieldValueByName(name: "RC Cut") { + ... on ProjectV2ItemFieldDateValue { + date + } + } + } + } + } + } + } + `; + + const retrieveProjectIssuesFieldValuesResult: { + node: { + items: { + totalCount: number; + pageInfo: { + endCursor: string; + }; + nodes: { + id: string; + content: { + id: string; + }; + cutDate: { + date: string; + }; + }[]; + }; + }; + } = await octokit.graphql(retrieveProjectIssuesFieldValuesQuery, { + projectId, + }); + + const projectIssuesFieldValues: RawGithubProjectIssuesFieldValues = retrieveProjectIssuesFieldValuesResult.node.items; + + return projectIssuesFieldValues; +} + +// This function retrieves a Github Project's issue field values recursively +export async function retrieveGithubProjectIssueFieldValuesRecursively( + nbFetches: number, + octokit: InstanceType, + projectId: string, + issueId: string, + cursor: string | undefined, +): Promise { + if (nbFetches >= MAX_NB_FETCHES) { + throw new Error(`Forbidden: Trying to do more than ${MAX_NB_FETCHES} fetches (${nbFetches}).`); + } + + const projectIssuesFieldValuesResponse: RawGithubProjectIssuesFieldValues = await retrieveGithubProjectIssuesFieldValues( + octokit, + projectId, + cursor, + ); + + const projectIssueFieldValuesResponseWithSameId: RawGithubProjectIssueFieldValues | undefined = + projectIssuesFieldValuesResponse.nodes.find( + (issue) => issue.content?.id === issueId + ); // 'issue.content' can be equal to null in edge case where the Github Project board includes private repo issues that can't be accessed by the access token we're using + + if (projectIssueFieldValuesResponseWithSameId) { + const projectIssueFieldValues: GithubProjectIssueFieldValues = { + id: projectIssueFieldValuesResponseWithSameId.content?.id, + itemId: projectIssueFieldValuesResponseWithSameId.id, + cutDate: projectIssueFieldValuesResponseWithSameId.cutDate?.date, + }; + return projectIssueFieldValues; + } + + const newCursor = projectIssuesFieldValuesResponse.pageInfo.endCursor; + if (newCursor) { + return await retrieveGithubProjectIssueFieldValuesRecursively(nbFetches + 1, octokit, projectId, issueId, newCursor); + } else { + return undefined; + } +} + +// This function adds an issue to a Github Project +export async function addIssueToGithubProject( + octokit: InstanceType, + projectId: string, + issueId: string, +): Promise { + const addIssueToProjectMutation = ` + mutation ($projectId: ID!, $contentId: ID!) { + addProjectV2ItemById(input: {projectId: $projectId, contentId: $contentId}) { + clientMutationId + } + } + `; + + await octokit.graphql(addIssueToProjectMutation, { + projectId: projectId, + contentId: issueId, + }); +} + +// This function updates Github Project issue's date field value +export async function updateGithubProjectDateFieldValue( + octokit: InstanceType, + projectId: string, + projectFieldId: string, + issueId: string, + newDatePropertyValue: string, +): Promise { + if (!isValidDateFormat(newDatePropertyValue)) { + throw new Error(`Invalid input: date ${newDatePropertyValue} doesn't match "YYYY-MM-DD" format.`); + } + + const issue: GithubProjectIssueFieldValues | undefined = await retrieveGithubProjectIssueFieldValuesRecursively( + 0, + octokit, + projectId, + issueId, + undefined, + ); + + if (!issue) { + throw new Error(`Issue with ID ${issueId} was not found on Github Project with ID ${projectId}.`); + } + + const updateGithubProjectDatePropertyMutation = ` + mutation ($projectId: ID!, $itemId: ID!, $fieldId: ID!, $date: Date!) { + updateProjectV2ItemFieldValue( + input: { + projectId: $projectId + itemId: $itemId + fieldId: $fieldId + value: { date: $date } + } + ) { + projectV2Item { + id + } + } + } + `; + + await octokit.graphql(updateGithubProjectDatePropertyMutation, { + projectId: projectId, + itemId: issue.itemId, + fieldId: projectFieldId, + date: newDatePropertyValue, + }); +} diff --git a/.github/scripts/shared/repo.ts b/.github/scripts/shared/repo.ts index 09e475350016..d87a7fb5a198 100644 --- a/.github/scripts/shared/repo.ts +++ b/.github/scripts/shared/repo.ts @@ -25,5 +25,9 @@ export async function retrieveRepo( const repoId = retrieveRepoResult?.repository?.id; + if (!repoId) { + throw new Error(`Repo with owner ${repoOwner} and name ${repoName} was not found.`); + } + return repoId; } diff --git a/.github/scripts/shared/utils.ts b/.github/scripts/shared/utils.ts new file mode 100644 index 000000000000..6ecc4f4d092a --- /dev/null +++ b/.github/scripts/shared/utils.ts @@ -0,0 +1,50 @@ +// This helper function checks if version has the correct format: "x.y.z" where "x", "y" and "z" are numbers. +export function isValidVersionFormat(str: string): boolean { + const regex = /^\d+\.\d+\.\d+$/; + return regex.test(str); +} + +// This helper function checks if a string has the date format "YYYY-MM-DD". +export function isValidDateFormat(dateString: string): boolean { + // Regular expression to match the date format "YYYY-MM-DD" + const dateFormatRegex = /^\d{4}-\d{2}-\d{2}$/; + + // Check if the dateString matches the regex + if (!dateFormatRegex.test(dateString)) { + return false; + } + + // Parse the date components + const [year, month, day] = dateString.split('-').map(Number); + + // Check if the date components form a valid date + const date = new Date(year, month - 1, day); + return ( + date.getFullYear() === year && + date.getMonth() === month - 1 && + date.getDate() === day + ); +} + +// This helper function generates the current date in that format: "YYYY-MM-DD" +export function getCurrentDateFormatted(): string { + const date = new Date(); + + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are zero-based, so add 1 + const day = String(date.getDate()).padStart(2, '0'); + + return `${year}-${month}-${day}`; +} + +// This mapping is used to know what planning repo is used for each code repo +export const codeRepoToPlanningRepo: { [key: string]: string } = { + "metamask-extension": "MetaMask-planning", + "metamask-mobile": "mobile-planning" +} + +// This mapping is used to know what platform each code repo is used for +export const codeRepoToPlatform: { [key: string]: string } = { + "metamask-extension": "extension", + "metamask-mobile": "mobile", +} diff --git a/.github/workflows/add-release-label.yml b/.github/workflows/add-release-label.yml index 2e34ad491b44..53f73c730696 100644 --- a/.github/workflows/add-release-label.yml +++ b/.github/workflows/add-release-label.yml @@ -18,7 +18,7 @@ jobs: fetch-depth: 0 # This is needed to checkout all branches - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Get the next semver version id: get-next-semver-version diff --git a/.github/workflows/build-beta.yml b/.github/workflows/build-beta.yml index 98a291edbb4f..c39228d30a69 100644 --- a/.github/workflows/build-beta.yml +++ b/.github/workflows/build-beta.yml @@ -42,7 +42,7 @@ jobs: - name: Setup environment if: ${{ steps.needs-beta-build.outputs.NEEDS_BETA_BUILD == 'true' }} - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Run beta build if: ${{ steps.needs-beta-build.outputs.NEEDS_BETA_BUILD == 'true' }} diff --git a/.github/workflows/build-storybook.yml b/.github/workflows/build-storybook.yml index e28b32556dbe..56ae72f4f02c 100644 --- a/.github/workflows/build-storybook.yml +++ b/.github/workflows/build-storybook.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v4 - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Build storybook run: yarn storybook:build diff --git a/.github/workflows/check-attributions.yml b/.github/workflows/check-attributions.yml index cadaf0d1141b..af9bd7e1b886 100644 --- a/.github/workflows/check-attributions.yml +++ b/.github/workflows/check-attributions.yml @@ -12,7 +12,7 @@ jobs: uses: actions/checkout@v4 - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Check attributions changes run: yarn attributions:check diff --git a/.github/workflows/check-pr-labels.yml b/.github/workflows/check-pr-labels.yml index cc492c09319e..e564e0543aad 100644 --- a/.github/workflows/check-pr-labels.yml +++ b/.github/workflows/check-pr-labels.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v4 - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Check PR has required labels env: diff --git a/.github/workflows/check-template-and-add-labels.yml b/.github/workflows/check-template-and-add-labels.yml index e9c1c2844ca5..232b5e5d8a63 100644 --- a/.github/workflows/check-template-and-add-labels.yml +++ b/.github/workflows/check-template-and-add-labels.yml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v4 - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Check template and add labels id: check-template-and-add-labels diff --git a/.github/workflows/close-bug-report.yml b/.github/workflows/close-bug-report.yml index 46dcd0a9e9ff..328c5b4429b6 100644 --- a/.github/workflows/close-bug-report.yml +++ b/.github/workflows/close-bug-report.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v4 - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Close release bug report issue env: diff --git a/.github/workflows/codespaces.yml b/.github/workflows/codespaces.yml deleted file mode 100644 index 5d37ba2d3dc3..000000000000 --- a/.github/workflows/codespaces.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: 'Codespaces: update yarn cache' - -on: - push: - branches: - - 'codespaces**' - - 'main' - paths: - - '**/yarn.lock' - -jobs: - yarn-cache: - name: Generate cache image - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main diff --git a/.github/workflows/create-bug-report.yml b/.github/workflows/create-bug-report.yml index c5164af00c02..65ff2f4ab9e5 100644 --- a/.github/workflows/create-bug-report.yml +++ b/.github/workflows/create-bug-report.yml @@ -12,24 +12,24 @@ jobs: if [[ "$GITHUB_REF" =~ ^refs/heads/Version-v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then version="${GITHUB_REF#refs/heads/Version-v}" echo "New release branch($version), continue next steps" - echo "version=$version" >> "$GITHUB_ENV" + echo "version=$version" >> "$GITHUB_OUTPUT" else echo "Not a release branch, skip next steps" fi + - name: Checkout repository + if: steps.extract_version.outputs.version + uses: actions/checkout@v4 + + - name: Setup environment + if: steps.extract_version.outputs.version + uses: metamask/github-tools/.github/actions/setup-environment@main + - name: Create bug report issue on planning repo - if: env.version - run: | - payload=$(cat <> "$GITHUB_OUTPUT" @@ -81,7 +81,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.issue.number }} - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Generate Attributions run: yarn attributions:generate - name: Cache attributions file @@ -182,4 +182,4 @@ jobs: env: 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 }}" + ACTION_RUN_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' diff --git a/.github/workflows/update-lavamoat-policies.yml b/.github/workflows/update-lavamoat-policies.yml index 09ab6b304a6a..d138733937cb 100644 --- a/.github/workflows/update-lavamoat-policies.yml +++ b/.github/workflows/update-lavamoat-policies.yml @@ -59,7 +59,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} PR_NUMBER: ${{ github.event.issue.number }} - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Get commit SHA id: commit-sha run: echo "COMMIT_SHA=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT" @@ -78,7 +78,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} PR_NUMBER: ${{ github.event.issue.number }} - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Update LavaMoat build policy run: yarn lavamoat:build:auto - name: Cache build policy @@ -107,7 +107,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} PR_NUMBER: ${{ github.event.issue.number }} - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Restore build policy uses: actions/cache/restore@v4 with: @@ -264,4 +264,4 @@ jobs: env: 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 }}" + ACTION_RUN_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' diff --git a/.github/workflows/validate-lavamoat-allow-scripts.yml b/.github/workflows/validate-lavamoat-allow-scripts.yml index 637a2d9aeb54..df680b2bb5d0 100644 --- a/.github/workflows/validate-lavamoat-allow-scripts.yml +++ b/.github/workflows/validate-lavamoat-allow-scripts.yml @@ -12,7 +12,7 @@ jobs: uses: actions/checkout@v4 - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Validate allow-scripts config run: yarn allow-scripts auto diff --git a/.github/workflows/validate-lavamoat-policy-build.yml b/.github/workflows/validate-lavamoat-policy-build.yml index 4524cc26a546..eed9d3f471bd 100644 --- a/.github/workflows/validate-lavamoat-policy-build.yml +++ b/.github/workflows/validate-lavamoat-policy-build.yml @@ -12,7 +12,7 @@ jobs: uses: actions/checkout@v4 - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Validate lavamoat build policy run: yarn lavamoat:build:auto diff --git a/.github/workflows/validate-lavamoat-policy-webapp.yml b/.github/workflows/validate-lavamoat-policy-webapp.yml index 37ff9ede00fc..2daa15f00cb9 100644 --- a/.github/workflows/validate-lavamoat-policy-webapp.yml +++ b/.github/workflows/validate-lavamoat-policy-webapp.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v4 - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Validate lavamoat ${{ matrix.build-type }} policy run: yarn lavamoat:webapp:auto:ci --build-types=${{ matrix.build-type }} diff --git a/.github/workflows/validate-page-object-usage.yml b/.github/workflows/validate-page-object-usage.yml index 910ee907cace..c44c96067900 100644 --- a/.github/workflows/validate-page-object-usage.yml +++ b/.github/workflows/validate-page-object-usage.yml @@ -2,6 +2,8 @@ name: Validate E2E Page Object usage on modified files on: pull_request: + branches: + - main types: - opened - reopened @@ -15,7 +17,7 @@ jobs: uses: actions/checkout@v4 - name: Setup environment - uses: metamask/github-tools/.github/actions/setup-environment@main + uses: metamask/github-tools/.github/actions/setup-environment@1d657e262aea7e3f216754febb624831527d2565 - name: Run E2E Page Object Usage Validation env: @@ -24,4 +26,4 @@ jobs: BRANCH: ${{ github.head_ref || github.ref_name }} HEAD_COMMIT_HASH: ${{ github.event.pull_request.head.sha || github.sha }} run: | - yarn validate-e2e-page-object-usage \ No newline at end of file + yarn validate-e2e-page-object-usage diff --git a/.github/workflows/wait-for-circleci-workflow-status.yml b/.github/workflows/wait-for-circleci-workflow-status.yml index 30efc6d35776..9c3c58f3aabc 100644 --- a/.github/workflows/wait-for-circleci-workflow-status.yml +++ b/.github/workflows/wait-for-circleci-workflow-status.yml @@ -30,7 +30,8 @@ jobs: done fi - if [ "$workflow_status" != "success" ]; then + # The "not_run" only happens when you're developing CI workflows and testing weird cases + if [ "$workflow_status" != "success" ] && [ "$workflow_status" != "not_run" ]; then echo "::error::Workflow status is '$workflow_status'. Exiting with error." exit 1 fi diff --git a/.yarn/patches/@metamask-providers-npm-19.0.0-3d962c6f1a.patch b/.yarn/patches/@metamask-providers-npm-19.0.0-3d962c6f1a.patch deleted file mode 100644 index 0133e49c9f65..000000000000 --- a/.yarn/patches/@metamask-providers-npm-19.0.0-3d962c6f1a.patch +++ /dev/null @@ -1,764 +0,0 @@ -diff --git a/dist/BaseProvider.cjs b/dist/BaseProvider.cjs -index bcb7967af0b6369cc843a159e1c369e5d8a91f12..e476b71774dd89b580f74a5598413bd96c248d26 100644 ---- a/dist/BaseProvider.cjs -+++ b/dist/BaseProvider.cjs -@@ -161,6 +161,7 @@ class BaseProvider extends safe_event_emitter_1.default { - * @param initialState.chainId - The chain ID. - * @param initialState.isUnlocked - Whether the user has unlocked MetaMask. - * @param initialState.networkVersion - The network version. -+ * @param initialState.isConnected - Whether the network is available. - * @fires BaseProvider#_initialized - If `initialState` is defined. - * @fires BaseProvider#connect - If `initialState` is defined. - */ -@@ -169,10 +170,10 @@ class BaseProvider extends safe_event_emitter_1.default { - throw new Error('Provider already initialized.'); - } - if (initialState) { -- const { accounts, chainId, isUnlocked, networkVersion } = initialState; -+ const { accounts, chainId, isUnlocked, networkVersion, isConnected } = initialState; - // EIP-1193 connect -- this._handleConnect(chainId); -- this._handleChainChanged({ chainId, networkVersion }); -+ this._handleConnect({ chainId, isConnected }); -+ this._handleChainChanged({ chainId, networkVersion, isConnected }); - this._handleUnlockStateChanged({ accounts, isUnlocked }); - this._handleAccountsChanged(accounts); - } -@@ -211,11 +212,13 @@ class BaseProvider extends safe_event_emitter_1.default { - * When the provider becomes connected, updates internal state and emits - * required events. Idempotent. - * -- * @param chainId - The ID of the newly connected chain. -+ * @param networkInfo - The options object. -+ * @param networkInfo.chainId - The ID of the newly connected chain. -+ * @param networkInfo.isConnected - Whether the network is available. - * @fires MetaMaskInpageProvider#connect - */ -- _handleConnect(chainId) { -- if (!this._state.isConnected) { -+ _handleConnect({ chainId, isConnected, }) { -+ if (!this._state.isConnected && isConnected) { - this._state.isConnected = true; - this.emit('connect', { chainId }); - this._log.debug(messages_1.default.info.connected(chainId)); -@@ -266,13 +269,14 @@ class BaseProvider extends safe_event_emitter_1.default { - * @fires BaseProvider#chainChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- _handleChainChanged({ chainId, } = {}) { -+ _handleChainChanged({ chainId, isConnected, } = {}) { - if (!(0, utils_1.isValidChainId)(chainId)) { - this._log.error(messages_1.default.errors.invalidNetworkParams(), { chainId }); - return; - } -- this._handleConnect(chainId); -+ this._handleConnect({ chainId, isConnected }); - if (chainId !== __classPrivateFieldGet(this, _BaseProvider_chainId, "f")) { - __classPrivateFieldSet(this, _BaseProvider_chainId, chainId, "f"); - if (this._state.initialized) { -diff --git a/dist/BaseProvider.cjs.map b/dist/BaseProvider.cjs.map -index 14b1303e2ad0e18c703db24c6ef086d6f6b4fafa..b21ebfa0ab7e7735945c5f86291e5d228cfe20a1 100644 ---- a/dist/BaseProvider.cjs.map -+++ b/dist/BaseProvider.cjs.map -@@ -1 +1 @@ --{"version":3,"file":"BaseProvider.cjs","sourceRoot":"","sources":["../src/BaseProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { JsonRpcEngine } from '@metamask/json-rpc-engine';\nimport { rpcErrors, JsonRpcError } from '@metamask/rpc-errors';\nimport SafeEventEmitter from '@metamask/safe-event-emitter';\nimport type {\n JsonRpcRequest,\n JsonRpcId,\n JsonRpcVersion2,\n JsonRpcSuccess,\n JsonRpcParams,\n Json,\n} from '@metamask/utils';\nimport dequal from 'fast-deep-equal';\n\nimport messages from './messages';\nimport type { ConsoleLike, Maybe } from './utils';\nimport { getRpcPromiseCallback, isValidChainId } from './utils';\n\nexport type UnvalidatedJsonRpcRequest = {\n id?: JsonRpcId;\n jsonrpc?: JsonRpcVersion2;\n method: string;\n params?: unknown;\n};\n\nexport type BaseProviderOptions = {\n /**\n * The logging API to use.\n */\n logger?: ConsoleLike;\n\n /**\n * The maximum number of event listeners.\n */\n maxEventListeners?: number;\n\n /**\n * `@metamask/json-rpc-engine` middleware. The middleware will be inserted in the given\n * order immediately after engine initialization.\n */\n rpcMiddleware?: JsonRpcMiddleware[];\n};\n\nexport type RequestArguments = {\n /** The RPC method to request. */\n method: string;\n\n /** The params of the RPC method, if any. */\n params?: unknown[] | Record;\n};\n\nexport type BaseProviderState = {\n accounts: null | string[];\n isConnected: boolean;\n isUnlocked: boolean;\n initialized: boolean;\n isPermanentlyDisconnected: boolean;\n};\n\n/**\n * An abstract class implementing the EIP-1193 interface. Implementers must:\n *\n * 1. At initialization, push a middleware to the internal `_rpcEngine` that\n * hands off requests to the server and receives responses in return.\n * 2. At initialization, retrieve initial state and call\n * {@link BaseProvider._initializeState} **once**.\n * 3. Ensure that the provider's state is synchronized with the wallet.\n * 4. Ensure that notifications are received and emitted as appropriate.\n */\nexport abstract class BaseProvider extends SafeEventEmitter {\n protected readonly _log: ConsoleLike;\n\n protected _state: BaseProviderState;\n\n protected _rpcEngine: JsonRpcEngine;\n\n protected static _defaultState: BaseProviderState = {\n accounts: null,\n isConnected: false,\n isUnlocked: false,\n initialized: false,\n isPermanentlyDisconnected: false,\n };\n\n /**\n * The chain ID of the currently connected Ethereum chain.\n * See [chainId.network]{@link https://chainid.network} for more information.\n */\n #chainId: string | null;\n\n /**\n * The user's currently selected Ethereum address.\n * If null, MetaMask is either locked or the user has not permitted any\n * addresses to be viewed.\n */\n #selectedAddress: string | null;\n\n /**\n * Create a new instance of the provider.\n *\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.rpcMiddleware - The RPC middleware stack. Default: [].\n */\n constructor({\n logger = console,\n maxEventListeners = 100,\n rpcMiddleware = [],\n }: BaseProviderOptions = {}) {\n super();\n\n this._log = logger;\n\n this.setMaxListeners(maxEventListeners);\n\n // Private state\n this._state = {\n ...BaseProvider._defaultState,\n };\n\n // Public state\n this.#selectedAddress = null;\n this.#chainId = null;\n\n // Bind functions to prevent consumers from making unbound calls\n this._handleAccountsChanged = this._handleAccountsChanged.bind(this);\n this._handleConnect = this._handleConnect.bind(this);\n this._handleChainChanged = this._handleChainChanged.bind(this);\n this._handleDisconnect = this._handleDisconnect.bind(this);\n this._handleUnlockStateChanged = this._handleUnlockStateChanged.bind(this);\n this._rpcRequest = this._rpcRequest.bind(this);\n this.request = this.request.bind(this);\n\n // Handle RPC requests via dapp-side RPC engine.\n //\n // ATTN: Implementers must push a middleware that hands off requests to\n // the server.\n const rpcEngine = new JsonRpcEngine();\n rpcMiddleware.forEach((middleware) => rpcEngine.push(middleware));\n this._rpcEngine = rpcEngine;\n }\n\n //====================\n // Public Properties\n //====================\n\n get chainId(): string | null {\n return this.#chainId;\n }\n\n get selectedAddress(): string | null {\n return this.#selectedAddress;\n }\n\n //====================\n // Public Methods\n //====================\n\n /**\n * Returns whether the provider can process RPC requests.\n *\n * @returns Whether the provider can process RPC requests.\n */\n isConnected(): boolean {\n return this._state.isConnected;\n }\n\n /**\n * Submits an RPC request for the given method, with the given params.\n * Resolves with the result of the method call, or rejects on error.\n *\n * @param args - The RPC request arguments.\n * @param args.method - The RPC method name.\n * @param args.params - The parameters for the RPC method.\n * @returns A Promise that resolves with the result of the RPC method,\n * or rejects if an error is encountered.\n */\n async request(args: RequestArguments): Promise> {\n if (!args || typeof args !== 'object' || Array.isArray(args)) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestArgs(),\n data: args,\n });\n }\n\n const { method, params } = args;\n\n if (typeof method !== 'string' || method.length === 0) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestMethod(),\n data: args,\n });\n }\n\n if (\n params !== undefined &&\n !Array.isArray(params) &&\n (typeof params !== 'object' || params === null)\n ) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestParams(),\n data: args,\n });\n }\n\n const payload =\n params === undefined || params === null\n ? {\n method,\n }\n : {\n method,\n params,\n };\n\n return new Promise((resolve, reject) => {\n this._rpcRequest(payload, getRpcPromiseCallback(resolve, reject));\n });\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * MUST be called by child classes.\n *\n * Sets initial state if provided and marks this provider as initialized.\n * Throws if called more than once.\n *\n * Permits the `networkVersion` field in the parameter object for\n * compatibility with child classes that use this value.\n *\n * @param initialState - The provider's initial state.\n * @param initialState.accounts - The user's accounts.\n * @param initialState.chainId - The chain ID.\n * @param initialState.isUnlocked - Whether the user has unlocked MetaMask.\n * @param initialState.networkVersion - The network version.\n * @fires BaseProvider#_initialized - If `initialState` is defined.\n * @fires BaseProvider#connect - If `initialState` is defined.\n */\n protected _initializeState(initialState?: {\n accounts: string[];\n chainId: string;\n isUnlocked: boolean;\n networkVersion?: string;\n }) {\n if (this._state.initialized) {\n throw new Error('Provider already initialized.');\n }\n\n if (initialState) {\n const { accounts, chainId, isUnlocked, networkVersion } = initialState;\n\n // EIP-1193 connect\n this._handleConnect(chainId);\n this._handleChainChanged({ chainId, networkVersion });\n this._handleUnlockStateChanged({ accounts, isUnlocked });\n this._handleAccountsChanged(accounts);\n }\n\n // Mark provider as initialized regardless of whether initial state was\n // retrieved.\n this._state.initialized = true;\n this.emit('_initialized');\n }\n\n /**\n * Internal RPC method. Forwards requests to background via the RPC engine.\n * Also remap ids inbound and outbound.\n *\n * @param payload - The RPC request object.\n * @param callback - The consumer's callback.\n * @returns The result of the RPC request.\n */\n protected _rpcRequest(\n payload: UnvalidatedJsonRpcRequest | UnvalidatedJsonRpcRequest[],\n callback: (...args: any[]) => void,\n ) {\n let callbackWrapper = callback;\n\n if (!Array.isArray(payload)) {\n if (!payload.jsonrpc) {\n payload.jsonrpc = '2.0';\n }\n\n if (\n payload.method === 'eth_accounts' ||\n payload.method === 'eth_requestAccounts'\n ) {\n // handle accounts changing\n callbackWrapper = (\n error: Error,\n response: JsonRpcSuccess,\n ) => {\n this._handleAccountsChanged(\n response.result ?? [],\n payload.method === 'eth_accounts',\n );\n callback(error, response);\n };\n }\n return this._rpcEngine.handle(payload as JsonRpcRequest, callbackWrapper);\n }\n return this._rpcEngine.handle(payload as JsonRpcRequest[], callbackWrapper);\n }\n\n /**\n * When the provider becomes connected, updates internal state and emits\n * required events. Idempotent.\n *\n * @param chainId - The ID of the newly connected chain.\n * @fires MetaMaskInpageProvider#connect\n */\n protected _handleConnect(chainId: string) {\n if (!this._state.isConnected) {\n this._state.isConnected = true;\n this.emit('connect', { chainId });\n this._log.debug(messages.info.connected(chainId));\n }\n }\n\n /**\n * When the provider becomes disconnected, updates internal state and emits\n * required events. Idempotent with respect to the isRecoverable parameter.\n *\n * Error codes per the CloseEvent status codes as required by EIP-1193:\n * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes.\n *\n * @param isRecoverable - Whether the disconnection is recoverable.\n * @param errorMessage - A custom error message.\n * @fires BaseProvider#disconnect - If the disconnection is not recoverable.\n */\n protected _handleDisconnect(isRecoverable: boolean, errorMessage?: string) {\n if (\n this._state.isConnected ||\n (!this._state.isPermanentlyDisconnected && !isRecoverable)\n ) {\n this._state.isConnected = false;\n\n let error;\n if (isRecoverable) {\n error = new JsonRpcError(\n 1013, // Try again later\n errorMessage ?? messages.errors.disconnected(),\n );\n this._log.debug(error);\n } else {\n error = new JsonRpcError(\n 1011, // Internal error\n errorMessage ?? messages.errors.permanentlyDisconnected(),\n );\n this._log.error(error);\n this.#chainId = null;\n this._state.accounts = null;\n this.#selectedAddress = null;\n this._state.isUnlocked = false;\n this._state.isPermanentlyDisconnected = true;\n }\n\n this.emit('disconnect', error);\n }\n }\n\n /**\n * Upon receipt of a new `chainId`, emits the corresponding event and sets\n * and sets relevant public state. Does nothing if the given `chainId` is\n * equivalent to the existing value.\n *\n * Permits the `networkVersion` field in the parameter object for\n * compatibility with child classes that use this value.\n *\n * @fires BaseProvider#chainChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n */\n protected _handleChainChanged({\n chainId,\n }:\n | { chainId?: string | undefined; networkVersion?: string | undefined }\n | undefined = {}) {\n if (!isValidChainId(chainId)) {\n this._log.error(messages.errors.invalidNetworkParams(), { chainId });\n return;\n }\n\n this._handleConnect(chainId);\n\n if (chainId !== this.#chainId) {\n this.#chainId = chainId;\n if (this._state.initialized) {\n this.emit('chainChanged', this.#chainId);\n }\n }\n }\n\n /**\n * Called when accounts may have changed. Diffs the new accounts value with\n * the current one, updates all state as necessary, and emits the\n * accountsChanged event.\n *\n * @param accounts - The new accounts value.\n * @param isEthAccounts - Whether the accounts value was returned by\n * a call to eth_accounts.\n */\n protected _handleAccountsChanged(\n accounts: unknown[],\n isEthAccounts = false,\n ): void {\n let _accounts = accounts;\n\n if (!Array.isArray(accounts)) {\n this._log.error(\n 'MetaMask: Received invalid accounts parameter. Please report this bug.',\n accounts,\n );\n _accounts = [];\n }\n\n for (const account of accounts) {\n if (typeof account !== 'string') {\n this._log.error(\n 'MetaMask: Received non-string account. Please report this bug.',\n accounts,\n );\n _accounts = [];\n break;\n }\n }\n\n // emit accountsChanged if anything about the accounts array has changed\n if (!dequal(this._state.accounts, _accounts)) {\n // we should always have the correct accounts even before eth_accounts\n // returns\n if (isEthAccounts && this._state.accounts !== null) {\n this._log.error(\n `MetaMask: 'eth_accounts' unexpectedly updated accounts. Please report this bug.`,\n _accounts,\n );\n }\n\n this._state.accounts = _accounts as string[];\n\n // handle selectedAddress\n if (this.#selectedAddress !== _accounts[0]) {\n this.#selectedAddress = (_accounts[0] as string) || null;\n }\n\n // finally, after all state has been updated, emit the event\n if (this._state.initialized) {\n const _nextAccounts = [..._accounts];\n this.emit('accountsChanged', _nextAccounts);\n }\n }\n }\n\n /**\n * Upon receipt of a new isUnlocked state, sets relevant public state.\n * Calls the accounts changed handler with the received accounts, or an empty\n * array.\n *\n * Does nothing if the received value is equal to the existing value.\n * There are no lock/unlock events.\n *\n * @param opts - Options bag.\n * @param opts.accounts - The exposed accounts, if any.\n * @param opts.isUnlocked - The latest isUnlocked value.\n */\n protected _handleUnlockStateChanged({\n accounts,\n isUnlocked,\n }: { accounts?: string[]; isUnlocked?: boolean } = {}) {\n if (typeof isUnlocked !== 'boolean') {\n this._log.error(\n 'MetaMask: Received invalid isUnlocked parameter. Please report this bug.',\n );\n return;\n }\n\n if (isUnlocked !== this._state.isUnlocked) {\n this._state.isUnlocked = isUnlocked;\n this._handleAccountsChanged(accounts ?? []);\n }\n }\n}\n"]} -\ No newline at end of file -+{"version":3,"file":"BaseProvider.cjs","sourceRoot":"","sources":["../src/BaseProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { JsonRpcEngine } from '@metamask/json-rpc-engine';\nimport { rpcErrors, JsonRpcError } from '@metamask/rpc-errors';\nimport SafeEventEmitter from '@metamask/safe-event-emitter';\nimport type {\n JsonRpcRequest,\n JsonRpcId,\n JsonRpcVersion2,\n JsonRpcSuccess,\n JsonRpcParams,\n Json,\n} from '@metamask/utils';\nimport dequal from 'fast-deep-equal';\n\nimport messages from './messages';\nimport type { ConsoleLike, Maybe } from './utils';\nimport { getRpcPromiseCallback, isValidChainId } from './utils';\n\nexport type UnvalidatedJsonRpcRequest = {\n id?: JsonRpcId;\n jsonrpc?: JsonRpcVersion2;\n method: string;\n params?: unknown;\n};\n\nexport type BaseProviderOptions = {\n /**\n * The logging API to use.\n */\n logger?: ConsoleLike;\n\n /**\n * The maximum number of event listeners.\n */\n maxEventListeners?: number;\n\n /**\n * `@metamask/json-rpc-engine` middleware. The middleware will be inserted in the given\n * order immediately after engine initialization.\n */\n rpcMiddleware?: JsonRpcMiddleware[];\n};\n\nexport type RequestArguments = {\n /** The RPC method to request. */\n method: string;\n\n /** The params of the RPC method, if any. */\n params?: unknown[] | Record;\n};\n\nexport type BaseProviderState = {\n accounts: null | string[];\n isConnected: boolean;\n isUnlocked: boolean;\n initialized: boolean;\n isPermanentlyDisconnected: boolean;\n};\n\n/**\n * An abstract class implementing the EIP-1193 interface. Implementers must:\n *\n * 1. At initialization, push a middleware to the internal `_rpcEngine` that\n * hands off requests to the server and receives responses in return.\n * 2. At initialization, retrieve initial state and call\n * {@link BaseProvider._initializeState} **once**.\n * 3. Ensure that the provider's state is synchronized with the wallet.\n * 4. Ensure that notifications are received and emitted as appropriate.\n */\nexport abstract class BaseProvider extends SafeEventEmitter {\n protected readonly _log: ConsoleLike;\n\n protected _state: BaseProviderState;\n\n protected _rpcEngine: JsonRpcEngine;\n\n protected static _defaultState: BaseProviderState = {\n accounts: null,\n isConnected: false,\n isUnlocked: false,\n initialized: false,\n isPermanentlyDisconnected: false,\n };\n\n /**\n * The chain ID of the currently connected Ethereum chain.\n * See [chainId.network]{@link https://chainid.network} for more information.\n */\n #chainId: string | null;\n\n /**\n * The user's currently selected Ethereum address.\n * If null, MetaMask is either locked or the user has not permitted any\n * addresses to be viewed.\n */\n #selectedAddress: string | null;\n\n /**\n * Create a new instance of the provider.\n *\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.rpcMiddleware - The RPC middleware stack. Default: [].\n */\n constructor({\n logger = console,\n maxEventListeners = 100,\n rpcMiddleware = [],\n }: BaseProviderOptions = {}) {\n super();\n\n this._log = logger;\n\n this.setMaxListeners(maxEventListeners);\n\n // Private state\n this._state = {\n ...BaseProvider._defaultState,\n };\n\n // Public state\n this.#selectedAddress = null;\n this.#chainId = null;\n\n // Bind functions to prevent consumers from making unbound calls\n this._handleAccountsChanged = this._handleAccountsChanged.bind(this);\n this._handleConnect = this._handleConnect.bind(this);\n this._handleChainChanged = this._handleChainChanged.bind(this);\n this._handleDisconnect = this._handleDisconnect.bind(this);\n this._handleUnlockStateChanged = this._handleUnlockStateChanged.bind(this);\n this._rpcRequest = this._rpcRequest.bind(this);\n this.request = this.request.bind(this);\n\n // Handle RPC requests via dapp-side RPC engine.\n //\n // ATTN: Implementers must push a middleware that hands off requests to\n // the server.\n const rpcEngine = new JsonRpcEngine();\n rpcMiddleware.forEach((middleware) => rpcEngine.push(middleware));\n this._rpcEngine = rpcEngine;\n }\n\n //====================\n // Public Properties\n //====================\n\n get chainId(): string | null {\n return this.#chainId;\n }\n\n get selectedAddress(): string | null {\n return this.#selectedAddress;\n }\n\n //====================\n // Public Methods\n //====================\n\n /**\n * Returns whether the provider can process RPC requests.\n *\n * @returns Whether the provider can process RPC requests.\n */\n isConnected(): boolean {\n return this._state.isConnected;\n }\n\n /**\n * Submits an RPC request for the given method, with the given params.\n * Resolves with the result of the method call, or rejects on error.\n *\n * @param args - The RPC request arguments.\n * @param args.method - The RPC method name.\n * @param args.params - The parameters for the RPC method.\n * @returns A Promise that resolves with the result of the RPC method,\n * or rejects if an error is encountered.\n */\n async request(args: RequestArguments): Promise> {\n if (!args || typeof args !== 'object' || Array.isArray(args)) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestArgs(),\n data: args,\n });\n }\n\n const { method, params } = args;\n\n if (typeof method !== 'string' || method.length === 0) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestMethod(),\n data: args,\n });\n }\n\n if (\n params !== undefined &&\n !Array.isArray(params) &&\n (typeof params !== 'object' || params === null)\n ) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestParams(),\n data: args,\n });\n }\n\n const payload =\n params === undefined || params === null\n ? {\n method,\n }\n : {\n method,\n params,\n };\n\n return new Promise((resolve, reject) => {\n this._rpcRequest(payload, getRpcPromiseCallback(resolve, reject));\n });\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * MUST be called by child classes.\n *\n * Sets initial state if provided and marks this provider as initialized.\n * Throws if called more than once.\n *\n * Permits the `networkVersion` field in the parameter object for\n * compatibility with child classes that use this value.\n *\n * @param initialState - The provider's initial state.\n * @param initialState.accounts - The user's accounts.\n * @param initialState.chainId - The chain ID.\n * @param initialState.isUnlocked - Whether the user has unlocked MetaMask.\n * @param initialState.networkVersion - The network version.\n * @param initialState.isConnected - Whether the network is available.\n * @fires BaseProvider#_initialized - If `initialState` is defined.\n * @fires BaseProvider#connect - If `initialState` is defined.\n */\n protected _initializeState(initialState?: {\n accounts: string[];\n chainId: string;\n isUnlocked: boolean;\n networkVersion?: string;\n isConnected?: boolean;\n }) {\n if (this._state.initialized) {\n throw new Error('Provider already initialized.');\n }\n\n if (initialState) {\n const { accounts, chainId, isUnlocked, networkVersion, isConnected } =\n initialState;\n\n // EIP-1193 connect\n this._handleConnect({ chainId, isConnected });\n this._handleChainChanged({ chainId, networkVersion, isConnected });\n this._handleUnlockStateChanged({ accounts, isUnlocked });\n this._handleAccountsChanged(accounts);\n }\n\n // Mark provider as initialized regardless of whether initial state was\n // retrieved.\n this._state.initialized = true;\n this.emit('_initialized');\n }\n\n /**\n * Internal RPC method. Forwards requests to background via the RPC engine.\n * Also remap ids inbound and outbound.\n *\n * @param payload - The RPC request object.\n * @param callback - The consumer's callback.\n * @returns The result of the RPC request.\n */\n protected _rpcRequest(\n payload: UnvalidatedJsonRpcRequest | UnvalidatedJsonRpcRequest[],\n callback: (...args: any[]) => void,\n ) {\n let callbackWrapper = callback;\n\n if (!Array.isArray(payload)) {\n if (!payload.jsonrpc) {\n payload.jsonrpc = '2.0';\n }\n\n if (\n payload.method === 'eth_accounts' ||\n payload.method === 'eth_requestAccounts'\n ) {\n // handle accounts changing\n callbackWrapper = (\n error: Error,\n response: JsonRpcSuccess,\n ) => {\n this._handleAccountsChanged(\n response.result ?? [],\n payload.method === 'eth_accounts',\n );\n callback(error, response);\n };\n }\n return this._rpcEngine.handle(payload as JsonRpcRequest, callbackWrapper);\n }\n return this._rpcEngine.handle(payload as JsonRpcRequest[], callbackWrapper);\n }\n\n /**\n * When the provider becomes connected, updates internal state and emits\n * required events. Idempotent.\n *\n * @param networkInfo - The options object.\n * @param networkInfo.chainId - The ID of the newly connected chain.\n * @param networkInfo.isConnected - Whether the network is available.\n * @fires MetaMaskInpageProvider#connect\n */\n protected _handleConnect({\n chainId,\n isConnected,\n }: {\n chainId: string;\n isConnected?: boolean | undefined;\n }) {\n if (!this._state.isConnected && isConnected) {\n this._state.isConnected = true;\n this.emit('connect', { chainId });\n this._log.debug(messages.info.connected(chainId));\n }\n }\n\n /**\n * When the provider becomes disconnected, updates internal state and emits\n * required events. Idempotent with respect to the isRecoverable parameter.\n *\n * Error codes per the CloseEvent status codes as required by EIP-1193:\n * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes.\n *\n * @param isRecoverable - Whether the disconnection is recoverable.\n * @param errorMessage - A custom error message.\n * @fires BaseProvider#disconnect - If the disconnection is not recoverable.\n */\n protected _handleDisconnect(isRecoverable: boolean, errorMessage?: string) {\n if (\n this._state.isConnected ||\n (!this._state.isPermanentlyDisconnected && !isRecoverable)\n ) {\n this._state.isConnected = false;\n\n let error;\n if (isRecoverable) {\n error = new JsonRpcError(\n 1013, // Try again later\n errorMessage ?? messages.errors.disconnected(),\n );\n this._log.debug(error);\n } else {\n error = new JsonRpcError(\n 1011, // Internal error\n errorMessage ?? messages.errors.permanentlyDisconnected(),\n );\n this._log.error(error);\n this.#chainId = null;\n this._state.accounts = null;\n this.#selectedAddress = null;\n this._state.isUnlocked = false;\n this._state.isPermanentlyDisconnected = true;\n }\n\n this.emit('disconnect', error);\n }\n }\n\n /**\n * Upon receipt of a new `chainId`, emits the corresponding event and sets\n * and sets relevant public state. Does nothing if the given `chainId` is\n * equivalent to the existing value.\n *\n * Permits the `networkVersion` field in the parameter object for\n * compatibility with child classes that use this value.\n *\n * @fires BaseProvider#chainChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n * @param networkInfo.isConnected - Whether the network is available.\n */\n protected _handleChainChanged({\n chainId,\n isConnected,\n }:\n | {\n chainId?: string;\n networkVersion?: string | undefined;\n isConnected?: boolean | undefined;\n }\n | undefined = {}) {\n if (!isValidChainId(chainId)) {\n this._log.error(messages.errors.invalidNetworkParams(), { chainId });\n return;\n }\n\n this._handleConnect({ chainId, isConnected });\n\n if (chainId !== this.#chainId) {\n this.#chainId = chainId;\n if (this._state.initialized) {\n this.emit('chainChanged', this.#chainId);\n }\n }\n }\n\n /**\n * Called when accounts may have changed. Diffs the new accounts value with\n * the current one, updates all state as necessary, and emits the\n * accountsChanged event.\n *\n * @param accounts - The new accounts value.\n * @param isEthAccounts - Whether the accounts value was returned by\n * a call to eth_accounts.\n */\n protected _handleAccountsChanged(\n accounts: unknown[],\n isEthAccounts = false,\n ): void {\n let _accounts = accounts;\n\n if (!Array.isArray(accounts)) {\n this._log.error(\n 'MetaMask: Received invalid accounts parameter. Please report this bug.',\n accounts,\n );\n _accounts = [];\n }\n\n for (const account of accounts) {\n if (typeof account !== 'string') {\n this._log.error(\n 'MetaMask: Received non-string account. Please report this bug.',\n accounts,\n );\n _accounts = [];\n break;\n }\n }\n\n // emit accountsChanged if anything about the accounts array has changed\n if (!dequal(this._state.accounts, _accounts)) {\n // we should always have the correct accounts even before eth_accounts\n // returns\n if (isEthAccounts && this._state.accounts !== null) {\n this._log.error(\n `MetaMask: 'eth_accounts' unexpectedly updated accounts. Please report this bug.`,\n _accounts,\n );\n }\n\n this._state.accounts = _accounts as string[];\n\n // handle selectedAddress\n if (this.#selectedAddress !== _accounts[0]) {\n this.#selectedAddress = (_accounts[0] as string) || null;\n }\n\n // finally, after all state has been updated, emit the event\n if (this._state.initialized) {\n const _nextAccounts = [..._accounts];\n this.emit('accountsChanged', _nextAccounts);\n }\n }\n }\n\n /**\n * Upon receipt of a new isUnlocked state, sets relevant public state.\n * Calls the accounts changed handler with the received accounts, or an empty\n * array.\n *\n * Does nothing if the received value is equal to the existing value.\n * There are no lock/unlock events.\n *\n * @param opts - Options bag.\n * @param opts.accounts - The exposed accounts, if any.\n * @param opts.isUnlocked - The latest isUnlocked value.\n */\n protected _handleUnlockStateChanged({\n accounts,\n isUnlocked,\n }: { accounts?: string[]; isUnlocked?: boolean } = {}) {\n if (typeof isUnlocked !== 'boolean') {\n this._log.error(\n 'MetaMask: Received invalid isUnlocked parameter. Please report this bug.',\n );\n return;\n }\n\n if (isUnlocked !== this._state.isUnlocked) {\n this._state.isUnlocked = isUnlocked;\n this._handleAccountsChanged(accounts ?? []);\n }\n }\n}\n"]} -\ No newline at end of file -diff --git a/dist/BaseProvider.d.cts b/dist/BaseProvider.d.cts -index 97187da7014911da7913f52893e6f754b1a46a2a..f729aef27f0c79dd7fbc8030200dd32f7cc95309 100644 ---- a/dist/BaseProvider.d.cts -+++ b/dist/BaseProvider.d.cts -@@ -96,6 +96,7 @@ export declare abstract class BaseProvider extends SafeEventEmitter { - * @param initialState.chainId - The chain ID. - * @param initialState.isUnlocked - Whether the user has unlocked MetaMask. - * @param initialState.networkVersion - The network version. -+ * @param initialState.isConnected - Whether the network is available. - * @fires BaseProvider#_initialized - If `initialState` is defined. - * @fires BaseProvider#connect - If `initialState` is defined. - */ -@@ -104,6 +105,7 @@ export declare abstract class BaseProvider extends SafeEventEmitter { - chainId: string; - isUnlocked: boolean; - networkVersion?: string; -+ isConnected?: boolean; - }): void; - /** - * Internal RPC method. Forwards requests to background via the RPC engine. -@@ -118,10 +120,15 @@ export declare abstract class BaseProvider extends SafeEventEmitter { - * When the provider becomes connected, updates internal state and emits - * required events. Idempotent. - * -- * @param chainId - The ID of the newly connected chain. -+ * @param networkInfo - The options object. -+ * @param networkInfo.chainId - The ID of the newly connected chain. -+ * @param networkInfo.isConnected - Whether the network is available. - * @fires MetaMaskInpageProvider#connect - */ -- protected _handleConnect(chainId: string): void; -+ protected _handleConnect({ chainId, isConnected, }: { -+ chainId: string; -+ isConnected?: boolean | undefined; -+ }): void; - /** - * When the provider becomes disconnected, updates internal state and emits - * required events. Idempotent with respect to the isRecoverable parameter. -@@ -145,10 +152,12 @@ export declare abstract class BaseProvider extends SafeEventEmitter { - * @fires BaseProvider#chainChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- protected _handleChainChanged({ chainId, }?: { -- chainId?: string | undefined; -+ protected _handleChainChanged({ chainId, isConnected, }?: { -+ chainId?: string; - networkVersion?: string | undefined; -+ isConnected?: boolean | undefined; - } | undefined): void; - /** - * Called when accounts may have changed. Diffs the new accounts value with -diff --git a/dist/BaseProvider.d.cts.map b/dist/BaseProvider.d.cts.map -index 9026d2115feee2d102e1367fab7084614c63b102..36543d383d3f8cbf6ad69652c59334e90471ebb6 100644 ---- a/dist/BaseProvider.d.cts.map -+++ b/dist/BaseProvider.d.cts.map -@@ -1 +1 @@ --{"version":3,"file":"BaseProvider.d.cts","sourceRoot":"","sources":["../src/BaseProvider.ts"],"names":[],"mappings":"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"} -\ No newline at end of file -+{"version":3,"file":"BaseProvider.d.cts","sourceRoot":"","sources":["../src/BaseProvider.ts"],"names":[],"mappings":"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"} -\ No newline at end of file -diff --git a/dist/BaseProvider.d.mts b/dist/BaseProvider.d.mts -index 98b6fae43b78c76d100452d5f53d129630672ed6..522ea6d7ed80856ef63c70e9c8fb9645a6f2ee10 100644 ---- a/dist/BaseProvider.d.mts -+++ b/dist/BaseProvider.d.mts -@@ -96,6 +96,7 @@ export declare abstract class BaseProvider extends SafeEventEmitter { - * @param initialState.chainId - The chain ID. - * @param initialState.isUnlocked - Whether the user has unlocked MetaMask. - * @param initialState.networkVersion - The network version. -+ * @param initialState.isConnected - Whether the network is available. - * @fires BaseProvider#_initialized - If `initialState` is defined. - * @fires BaseProvider#connect - If `initialState` is defined. - */ -@@ -104,6 +105,7 @@ export declare abstract class BaseProvider extends SafeEventEmitter { - chainId: string; - isUnlocked: boolean; - networkVersion?: string; -+ isConnected?: boolean; - }): void; - /** - * Internal RPC method. Forwards requests to background via the RPC engine. -@@ -118,10 +120,15 @@ export declare abstract class BaseProvider extends SafeEventEmitter { - * When the provider becomes connected, updates internal state and emits - * required events. Idempotent. - * -- * @param chainId - The ID of the newly connected chain. -+ * @param networkInfo - The options object. -+ * @param networkInfo.chainId - The ID of the newly connected chain. -+ * @param networkInfo.isConnected - Whether the network is available. - * @fires MetaMaskInpageProvider#connect - */ -- protected _handleConnect(chainId: string): void; -+ protected _handleConnect({ chainId, isConnected, }: { -+ chainId: string; -+ isConnected?: boolean | undefined; -+ }): void; - /** - * When the provider becomes disconnected, updates internal state and emits - * required events. Idempotent with respect to the isRecoverable parameter. -@@ -145,10 +152,12 @@ export declare abstract class BaseProvider extends SafeEventEmitter { - * @fires BaseProvider#chainChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- protected _handleChainChanged({ chainId, }?: { -- chainId?: string | undefined; -+ protected _handleChainChanged({ chainId, isConnected, }?: { -+ chainId?: string; - networkVersion?: string | undefined; -+ isConnected?: boolean | undefined; - } | undefined): void; - /** - * Called when accounts may have changed. Diffs the new accounts value with -diff --git a/dist/BaseProvider.d.mts.map b/dist/BaseProvider.d.mts.map -index a3f28b3fa7ff6f8d79235e5dbe9ee1d4ec7a03c7..22ecc4cadb232efdf9b4502e6e286750c7131a88 100644 ---- a/dist/BaseProvider.d.mts.map -+++ b/dist/BaseProvider.d.mts.map -@@ -1 +1 @@ --{"version":3,"file":"BaseProvider.d.mts","sourceRoot":"","sources":["../src/BaseProvider.ts"],"names":[],"mappings":"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"} -\ No newline at end of file -+{"version":3,"file":"BaseProvider.d.mts","sourceRoot":"","sources":["../src/BaseProvider.ts"],"names":[],"mappings":"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"} -\ No newline at end of file -diff --git a/dist/BaseProvider.mjs b/dist/BaseProvider.mjs -index 2a88b945be89d73d04a075508d6f2858a8c45feb..7bceb5f91ef9ce13c26145e190c29a092dc3f253 100644 ---- a/dist/BaseProvider.mjs -+++ b/dist/BaseProvider.mjs -@@ -162,6 +162,7 @@ export class BaseProvider extends SafeEventEmitter { - * @param initialState.chainId - The chain ID. - * @param initialState.isUnlocked - Whether the user has unlocked MetaMask. - * @param initialState.networkVersion - The network version. -+ * @param initialState.isConnected - Whether the network is available. - * @fires BaseProvider#_initialized - If `initialState` is defined. - * @fires BaseProvider#connect - If `initialState` is defined. - */ -@@ -170,10 +171,10 @@ export class BaseProvider extends SafeEventEmitter { - throw new Error('Provider already initialized.'); - } - if (initialState) { -- const { accounts, chainId, isUnlocked, networkVersion } = initialState; -+ const { accounts, chainId, isUnlocked, networkVersion, isConnected } = initialState; - // EIP-1193 connect -- this._handleConnect(chainId); -- this._handleChainChanged({ chainId, networkVersion }); -+ this._handleConnect({ chainId, isConnected }); -+ this._handleChainChanged({ chainId, networkVersion, isConnected }); - this._handleUnlockStateChanged({ accounts, isUnlocked }); - this._handleAccountsChanged(accounts); - } -@@ -212,11 +213,13 @@ export class BaseProvider extends SafeEventEmitter { - * When the provider becomes connected, updates internal state and emits - * required events. Idempotent. - * -- * @param chainId - The ID of the newly connected chain. -+ * @param networkInfo - The options object. -+ * @param networkInfo.chainId - The ID of the newly connected chain. -+ * @param networkInfo.isConnected - Whether the network is available. - * @fires MetaMaskInpageProvider#connect - */ -- _handleConnect(chainId) { -- if (!this._state.isConnected) { -+ _handleConnect({ chainId, isConnected, }) { -+ if (!this._state.isConnected && isConnected) { - this._state.isConnected = true; - this.emit('connect', { chainId }); - this._log.debug(messages.info.connected(chainId)); -@@ -267,13 +270,14 @@ export class BaseProvider extends SafeEventEmitter { - * @fires BaseProvider#chainChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- _handleChainChanged({ chainId, } = {}) { -+ _handleChainChanged({ chainId, isConnected, } = {}) { - if (!isValidChainId(chainId)) { - this._log.error(messages.errors.invalidNetworkParams(), { chainId }); - return; - } -- this._handleConnect(chainId); -+ this._handleConnect({ chainId, isConnected }); - if (chainId !== __classPrivateFieldGet(this, _BaseProvider_chainId, "f")) { - __classPrivateFieldSet(this, _BaseProvider_chainId, chainId, "f"); - if (this._state.initialized) { -diff --git a/dist/BaseProvider.mjs.map b/dist/BaseProvider.mjs.map -index c7b8596ea0a3dbad9937a4f242fe9f28cfe999ee..463c46c2907b0f2ea09519b01123fc1a673ffd2b 100644 ---- a/dist/BaseProvider.mjs.map -+++ b/dist/BaseProvider.mjs.map -@@ -1 +1 @@ --{"version":3,"file":"BaseProvider.mjs","sourceRoot":"","sources":["../src/BaseProvider.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AACA,OAAO,EAAE,aAAa,EAAE,kCAAkC;AAC1D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,6BAA6B;AAC/D,OAAO,gBAAgB,qCAAqC;AAS5D,OAAO,OAAM,wBAAwB;;AAErC,OAAO,QAAQ,uBAAmB;AAElC,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,oBAAgB;AA2ChE;;;;;;;;;GASG;AACH,MAAM,OAAgB,YAAa,SAAQ,gBAAgB;IA4BzD;;;;;;;;OAQG;IACH,YAAY,EACV,MAAM,GAAG,OAAO,EAChB,iBAAiB,GAAG,GAAG,EACvB,aAAa,GAAG,EAAE,MACK,EAAE;QACzB,KAAK,EAAE,CAAC;QA3BV;;;WAGG;QACH,wCAAwB;QAExB;;;;WAIG;QACH,gDAAgC;QAkB9B,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;QAEnB,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;QAExC,gBAAgB;QAChB,IAAI,CAAC,MAAM,GAAG;YACZ,GAAG,YAAY,CAAC,aAAa;SAC9B,CAAC;QAEF,eAAe;QACf,uBAAA,IAAI,iCAAoB,IAAI,MAAA,CAAC;QAC7B,uBAAA,IAAI,yBAAY,IAAI,MAAA,CAAC;QAErB,gEAAgE;QAChE,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEvC,gDAAgD;QAChD,EAAE;QACF,uEAAuE;QACvE,cAAc;QACd,MAAM,SAAS,GAAG,IAAI,aAAa,EAAE,CAAC;QACtC,aAAa,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,sBAAsB;IACtB,oBAAoB;IACpB,sBAAsB;IAEtB,IAAI,OAAO;QACT,OAAO,uBAAA,IAAI,6BAAS,CAAC;IACvB,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,uBAAA,IAAI,qCAAiB,CAAC;IAC/B,CAAC;IAED,sBAAsB;IACtB,iBAAiB;IACjB,sBAAsB;IAEtB;;;;OAIG;IACH,WAAW;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;IACjC,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,OAAO,CAAO,IAAsB;QACxC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7D,MAAM,SAAS,CAAC,cAAc,CAAC;gBAC7B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,kBAAkB,EAAE;gBAC7C,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;QACL,CAAC;QAED,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAEhC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtD,MAAM,SAAS,CAAC,cAAc,CAAC;gBAC7B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,oBAAoB,EAAE;gBAC/C,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;QACL,CAAC;QAED,IACE,MAAM,KAAK,SAAS;YACpB,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YACtB,CAAC,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,CAAC,EAC/C,CAAC;YACD,MAAM,SAAS,CAAC,cAAc,CAAC;gBAC7B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,oBAAoB,EAAE;gBAC/C,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GACX,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI;YACrC,CAAC,CAAC;gBACE,MAAM;aACP;YACH,CAAC,CAAC;gBACE,MAAM;gBACN,MAAM;aACP,CAAC;QAER,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,sBAAsB;IACtB,kBAAkB;IAClB,sBAAsB;IAEtB;;;;;;;;;;;;;;;;OAgBG;IACO,gBAAgB,CAAC,YAK1B;QACC,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,YAAY,CAAC;YAEvE,mBAAmB;YACnB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAC7B,IAAI,CAAC,mBAAmB,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,yBAAyB,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;YACzD,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC;QAED,uEAAuE;QACvE,aAAa;QACb,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC5B,CAAC;IAED;;;;;;;OAOG;IACO,WAAW,CACnB,OAAgE,EAChE,QAAkC;QAElC,IAAI,eAAe,GAAG,QAAQ,CAAC;QAE/B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBACrB,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;YAC1B,CAAC;YAED,IACE,OAAO,CAAC,MAAM,KAAK,cAAc;gBACjC,OAAO,CAAC,MAAM,KAAK,qBAAqB,EACxC,CAAC;gBACD,2BAA2B;gBAC3B,eAAe,GAAG,CAChB,KAAY,EACZ,QAAkC,EAClC,EAAE;oBACF,IAAI,CAAC,sBAAsB,CACzB,QAAQ,CAAC,MAAM,IAAI,EAAE,EACrB,OAAO,CAAC,MAAM,KAAK,cAAc,CAClC,CAAC;oBACF,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;gBAC5B,CAAC,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAyB,EAAE,eAAe,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAA2B,EAAE,eAAe,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;;OAMG;IACO,cAAc,CAAC,OAAe;QACtC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED;;;;;;;;;;OAUG;IACO,iBAAiB,CAAC,aAAsB,EAAE,YAAqB;QACvE,IACE,IAAI,CAAC,MAAM,CAAC,WAAW;YACvB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,yBAAyB,IAAI,CAAC,aAAa,CAAC,EAC1D,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC;YAEhC,IAAI,KAAK,CAAC;YACV,IAAI,aAAa,EAAE,CAAC;gBAClB,KAAK,GAAG,IAAI,YAAY,CACtB,IAAI,EAAE,kBAAkB;gBACxB,YAAY,IAAI,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,CAC/C,CAAC;gBACF,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACzB,CAAC;iBAAM,CAAC;gBACN,KAAK,GAAG,IAAI,YAAY,CACtB,IAAI,EAAE,iBAAiB;gBACvB,YAAY,IAAI,QAAQ,CAAC,MAAM,CAAC,uBAAuB,EAAE,CAC1D,CAAC;gBACF,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACvB,uBAAA,IAAI,yBAAY,IAAI,MAAA,CAAC;gBACrB,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC;gBAC5B,uBAAA,IAAI,iCAAoB,IAAI,MAAA,CAAC;gBAC7B,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,KAAK,CAAC;gBAC/B,IAAI,CAAC,MAAM,CAAC,yBAAyB,GAAG,IAAI,CAAC;YAC/C,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACO,mBAAmB,CAAC,EAC5B,OAAO,MAGO,EAAE;QAChB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,oBAAoB,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;YACrE,OAAO;QACT,CAAC;QAED,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAE7B,IAAI,OAAO,KAAK,uBAAA,IAAI,6BAAS,EAAE,CAAC;YAC9B,uBAAA,IAAI,yBAAY,OAAO,MAAA,CAAC;YACxB,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;gBAC5B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,uBAAA,IAAI,6BAAS,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACO,sBAAsB,CAC9B,QAAmB,EACnB,aAAa,GAAG,KAAK;QAErB,IAAI,SAAS,GAAG,QAAQ,CAAC;QAEzB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,KAAK,CACb,wEAAwE,EACxE,QAAQ,CACT,CAAC;YACF,SAAS,GAAG,EAAE,CAAC;QACjB,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAChC,IAAI,CAAC,IAAI,CAAC,KAAK,CACb,gEAAgE,EAChE,QAAQ,CACT,CAAC;gBACF,SAAS,GAAG,EAAE,CAAC;gBACf,MAAM;YACR,CAAC;QACH,CAAC;QAED,wEAAwE;QACxE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC;YAC7C,sEAAsE;YACtE,UAAU;YACV,IAAI,aAAa,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;gBACnD,IAAI,CAAC,IAAI,CAAC,KAAK,CACb,iFAAiF,EACjF,SAAS,CACV,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,SAAqB,CAAC;YAE7C,yBAAyB;YACzB,IAAI,uBAAA,IAAI,qCAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3C,uBAAA,IAAI,iCAAqB,SAAS,CAAC,CAAC,CAAY,IAAI,IAAI,MAAA,CAAC;YAC3D,CAAC;YAED,4DAA4D;YAC5D,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;gBAC5B,MAAM,aAAa,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;gBACrC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACO,yBAAyB,CAAC,EAClC,QAAQ,EACR,UAAU,MACuC,EAAE;QACnD,IAAI,OAAO,UAAU,KAAK,SAAS,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,KAAK,CACb,0EAA0E,CAC3E,CAAC;YACF,OAAO;QACT,CAAC;QAED,IAAI,UAAU,KAAK,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAC1C,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,UAAU,CAAC;YACpC,IAAI,CAAC,sBAAsB,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;;;AAzZgB,0BAAa,GAAsB;IAClD,QAAQ,EAAE,IAAI;IACd,WAAW,EAAE,KAAK;IAClB,UAAU,EAAE,KAAK;IACjB,WAAW,EAAE,KAAK;IAClB,yBAAyB,EAAE,KAAK;CACjC,AAN6B,CAM5B","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { JsonRpcEngine } from '@metamask/json-rpc-engine';\nimport { rpcErrors, JsonRpcError } from '@metamask/rpc-errors';\nimport SafeEventEmitter from '@metamask/safe-event-emitter';\nimport type {\n JsonRpcRequest,\n JsonRpcId,\n JsonRpcVersion2,\n JsonRpcSuccess,\n JsonRpcParams,\n Json,\n} from '@metamask/utils';\nimport dequal from 'fast-deep-equal';\n\nimport messages from './messages';\nimport type { ConsoleLike, Maybe } from './utils';\nimport { getRpcPromiseCallback, isValidChainId } from './utils';\n\nexport type UnvalidatedJsonRpcRequest = {\n id?: JsonRpcId;\n jsonrpc?: JsonRpcVersion2;\n method: string;\n params?: unknown;\n};\n\nexport type BaseProviderOptions = {\n /**\n * The logging API to use.\n */\n logger?: ConsoleLike;\n\n /**\n * The maximum number of event listeners.\n */\n maxEventListeners?: number;\n\n /**\n * `@metamask/json-rpc-engine` middleware. The middleware will be inserted in the given\n * order immediately after engine initialization.\n */\n rpcMiddleware?: JsonRpcMiddleware[];\n};\n\nexport type RequestArguments = {\n /** The RPC method to request. */\n method: string;\n\n /** The params of the RPC method, if any. */\n params?: unknown[] | Record;\n};\n\nexport type BaseProviderState = {\n accounts: null | string[];\n isConnected: boolean;\n isUnlocked: boolean;\n initialized: boolean;\n isPermanentlyDisconnected: boolean;\n};\n\n/**\n * An abstract class implementing the EIP-1193 interface. Implementers must:\n *\n * 1. At initialization, push a middleware to the internal `_rpcEngine` that\n * hands off requests to the server and receives responses in return.\n * 2. At initialization, retrieve initial state and call\n * {@link BaseProvider._initializeState} **once**.\n * 3. Ensure that the provider's state is synchronized with the wallet.\n * 4. Ensure that notifications are received and emitted as appropriate.\n */\nexport abstract class BaseProvider extends SafeEventEmitter {\n protected readonly _log: ConsoleLike;\n\n protected _state: BaseProviderState;\n\n protected _rpcEngine: JsonRpcEngine;\n\n protected static _defaultState: BaseProviderState = {\n accounts: null,\n isConnected: false,\n isUnlocked: false,\n initialized: false,\n isPermanentlyDisconnected: false,\n };\n\n /**\n * The chain ID of the currently connected Ethereum chain.\n * See [chainId.network]{@link https://chainid.network} for more information.\n */\n #chainId: string | null;\n\n /**\n * The user's currently selected Ethereum address.\n * If null, MetaMask is either locked or the user has not permitted any\n * addresses to be viewed.\n */\n #selectedAddress: string | null;\n\n /**\n * Create a new instance of the provider.\n *\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.rpcMiddleware - The RPC middleware stack. Default: [].\n */\n constructor({\n logger = console,\n maxEventListeners = 100,\n rpcMiddleware = [],\n }: BaseProviderOptions = {}) {\n super();\n\n this._log = logger;\n\n this.setMaxListeners(maxEventListeners);\n\n // Private state\n this._state = {\n ...BaseProvider._defaultState,\n };\n\n // Public state\n this.#selectedAddress = null;\n this.#chainId = null;\n\n // Bind functions to prevent consumers from making unbound calls\n this._handleAccountsChanged = this._handleAccountsChanged.bind(this);\n this._handleConnect = this._handleConnect.bind(this);\n this._handleChainChanged = this._handleChainChanged.bind(this);\n this._handleDisconnect = this._handleDisconnect.bind(this);\n this._handleUnlockStateChanged = this._handleUnlockStateChanged.bind(this);\n this._rpcRequest = this._rpcRequest.bind(this);\n this.request = this.request.bind(this);\n\n // Handle RPC requests via dapp-side RPC engine.\n //\n // ATTN: Implementers must push a middleware that hands off requests to\n // the server.\n const rpcEngine = new JsonRpcEngine();\n rpcMiddleware.forEach((middleware) => rpcEngine.push(middleware));\n this._rpcEngine = rpcEngine;\n }\n\n //====================\n // Public Properties\n //====================\n\n get chainId(): string | null {\n return this.#chainId;\n }\n\n get selectedAddress(): string | null {\n return this.#selectedAddress;\n }\n\n //====================\n // Public Methods\n //====================\n\n /**\n * Returns whether the provider can process RPC requests.\n *\n * @returns Whether the provider can process RPC requests.\n */\n isConnected(): boolean {\n return this._state.isConnected;\n }\n\n /**\n * Submits an RPC request for the given method, with the given params.\n * Resolves with the result of the method call, or rejects on error.\n *\n * @param args - The RPC request arguments.\n * @param args.method - The RPC method name.\n * @param args.params - The parameters for the RPC method.\n * @returns A Promise that resolves with the result of the RPC method,\n * or rejects if an error is encountered.\n */\n async request(args: RequestArguments): Promise> {\n if (!args || typeof args !== 'object' || Array.isArray(args)) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestArgs(),\n data: args,\n });\n }\n\n const { method, params } = args;\n\n if (typeof method !== 'string' || method.length === 0) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestMethod(),\n data: args,\n });\n }\n\n if (\n params !== undefined &&\n !Array.isArray(params) &&\n (typeof params !== 'object' || params === null)\n ) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestParams(),\n data: args,\n });\n }\n\n const payload =\n params === undefined || params === null\n ? {\n method,\n }\n : {\n method,\n params,\n };\n\n return new Promise((resolve, reject) => {\n this._rpcRequest(payload, getRpcPromiseCallback(resolve, reject));\n });\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * MUST be called by child classes.\n *\n * Sets initial state if provided and marks this provider as initialized.\n * Throws if called more than once.\n *\n * Permits the `networkVersion` field in the parameter object for\n * compatibility with child classes that use this value.\n *\n * @param initialState - The provider's initial state.\n * @param initialState.accounts - The user's accounts.\n * @param initialState.chainId - The chain ID.\n * @param initialState.isUnlocked - Whether the user has unlocked MetaMask.\n * @param initialState.networkVersion - The network version.\n * @fires BaseProvider#_initialized - If `initialState` is defined.\n * @fires BaseProvider#connect - If `initialState` is defined.\n */\n protected _initializeState(initialState?: {\n accounts: string[];\n chainId: string;\n isUnlocked: boolean;\n networkVersion?: string;\n }) {\n if (this._state.initialized) {\n throw new Error('Provider already initialized.');\n }\n\n if (initialState) {\n const { accounts, chainId, isUnlocked, networkVersion } = initialState;\n\n // EIP-1193 connect\n this._handleConnect(chainId);\n this._handleChainChanged({ chainId, networkVersion });\n this._handleUnlockStateChanged({ accounts, isUnlocked });\n this._handleAccountsChanged(accounts);\n }\n\n // Mark provider as initialized regardless of whether initial state was\n // retrieved.\n this._state.initialized = true;\n this.emit('_initialized');\n }\n\n /**\n * Internal RPC method. Forwards requests to background via the RPC engine.\n * Also remap ids inbound and outbound.\n *\n * @param payload - The RPC request object.\n * @param callback - The consumer's callback.\n * @returns The result of the RPC request.\n */\n protected _rpcRequest(\n payload: UnvalidatedJsonRpcRequest | UnvalidatedJsonRpcRequest[],\n callback: (...args: any[]) => void,\n ) {\n let callbackWrapper = callback;\n\n if (!Array.isArray(payload)) {\n if (!payload.jsonrpc) {\n payload.jsonrpc = '2.0';\n }\n\n if (\n payload.method === 'eth_accounts' ||\n payload.method === 'eth_requestAccounts'\n ) {\n // handle accounts changing\n callbackWrapper = (\n error: Error,\n response: JsonRpcSuccess,\n ) => {\n this._handleAccountsChanged(\n response.result ?? [],\n payload.method === 'eth_accounts',\n );\n callback(error, response);\n };\n }\n return this._rpcEngine.handle(payload as JsonRpcRequest, callbackWrapper);\n }\n return this._rpcEngine.handle(payload as JsonRpcRequest[], callbackWrapper);\n }\n\n /**\n * When the provider becomes connected, updates internal state and emits\n * required events. Idempotent.\n *\n * @param chainId - The ID of the newly connected chain.\n * @fires MetaMaskInpageProvider#connect\n */\n protected _handleConnect(chainId: string) {\n if (!this._state.isConnected) {\n this._state.isConnected = true;\n this.emit('connect', { chainId });\n this._log.debug(messages.info.connected(chainId));\n }\n }\n\n /**\n * When the provider becomes disconnected, updates internal state and emits\n * required events. Idempotent with respect to the isRecoverable parameter.\n *\n * Error codes per the CloseEvent status codes as required by EIP-1193:\n * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes.\n *\n * @param isRecoverable - Whether the disconnection is recoverable.\n * @param errorMessage - A custom error message.\n * @fires BaseProvider#disconnect - If the disconnection is not recoverable.\n */\n protected _handleDisconnect(isRecoverable: boolean, errorMessage?: string) {\n if (\n this._state.isConnected ||\n (!this._state.isPermanentlyDisconnected && !isRecoverable)\n ) {\n this._state.isConnected = false;\n\n let error;\n if (isRecoverable) {\n error = new JsonRpcError(\n 1013, // Try again later\n errorMessage ?? messages.errors.disconnected(),\n );\n this._log.debug(error);\n } else {\n error = new JsonRpcError(\n 1011, // Internal error\n errorMessage ?? messages.errors.permanentlyDisconnected(),\n );\n this._log.error(error);\n this.#chainId = null;\n this._state.accounts = null;\n this.#selectedAddress = null;\n this._state.isUnlocked = false;\n this._state.isPermanentlyDisconnected = true;\n }\n\n this.emit('disconnect', error);\n }\n }\n\n /**\n * Upon receipt of a new `chainId`, emits the corresponding event and sets\n * and sets relevant public state. Does nothing if the given `chainId` is\n * equivalent to the existing value.\n *\n * Permits the `networkVersion` field in the parameter object for\n * compatibility with child classes that use this value.\n *\n * @fires BaseProvider#chainChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n */\n protected _handleChainChanged({\n chainId,\n }:\n | { chainId?: string | undefined; networkVersion?: string | undefined }\n | undefined = {}) {\n if (!isValidChainId(chainId)) {\n this._log.error(messages.errors.invalidNetworkParams(), { chainId });\n return;\n }\n\n this._handleConnect(chainId);\n\n if (chainId !== this.#chainId) {\n this.#chainId = chainId;\n if (this._state.initialized) {\n this.emit('chainChanged', this.#chainId);\n }\n }\n }\n\n /**\n * Called when accounts may have changed. Diffs the new accounts value with\n * the current one, updates all state as necessary, and emits the\n * accountsChanged event.\n *\n * @param accounts - The new accounts value.\n * @param isEthAccounts - Whether the accounts value was returned by\n * a call to eth_accounts.\n */\n protected _handleAccountsChanged(\n accounts: unknown[],\n isEthAccounts = false,\n ): void {\n let _accounts = accounts;\n\n if (!Array.isArray(accounts)) {\n this._log.error(\n 'MetaMask: Received invalid accounts parameter. Please report this bug.',\n accounts,\n );\n _accounts = [];\n }\n\n for (const account of accounts) {\n if (typeof account !== 'string') {\n this._log.error(\n 'MetaMask: Received non-string account. Please report this bug.',\n accounts,\n );\n _accounts = [];\n break;\n }\n }\n\n // emit accountsChanged if anything about the accounts array has changed\n if (!dequal(this._state.accounts, _accounts)) {\n // we should always have the correct accounts even before eth_accounts\n // returns\n if (isEthAccounts && this._state.accounts !== null) {\n this._log.error(\n `MetaMask: 'eth_accounts' unexpectedly updated accounts. Please report this bug.`,\n _accounts,\n );\n }\n\n this._state.accounts = _accounts as string[];\n\n // handle selectedAddress\n if (this.#selectedAddress !== _accounts[0]) {\n this.#selectedAddress = (_accounts[0] as string) || null;\n }\n\n // finally, after all state has been updated, emit the event\n if (this._state.initialized) {\n const _nextAccounts = [..._accounts];\n this.emit('accountsChanged', _nextAccounts);\n }\n }\n }\n\n /**\n * Upon receipt of a new isUnlocked state, sets relevant public state.\n * Calls the accounts changed handler with the received accounts, or an empty\n * array.\n *\n * Does nothing if the received value is equal to the existing value.\n * There are no lock/unlock events.\n *\n * @param opts - Options bag.\n * @param opts.accounts - The exposed accounts, if any.\n * @param opts.isUnlocked - The latest isUnlocked value.\n */\n protected _handleUnlockStateChanged({\n accounts,\n isUnlocked,\n }: { accounts?: string[]; isUnlocked?: boolean } = {}) {\n if (typeof isUnlocked !== 'boolean') {\n this._log.error(\n 'MetaMask: Received invalid isUnlocked parameter. Please report this bug.',\n );\n return;\n }\n\n if (isUnlocked !== this._state.isUnlocked) {\n this._state.isUnlocked = isUnlocked;\n this._handleAccountsChanged(accounts ?? []);\n }\n }\n}\n"]} -\ No newline at end of file -+{"version":3,"file":"BaseProvider.mjs","sourceRoot":"","sources":["../src/BaseProvider.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AACA,OAAO,EAAE,aAAa,EAAE,kCAAkC;AAC1D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,6BAA6B;AAC/D,OAAO,gBAAgB,qCAAqC;AAS5D,OAAO,OAAM,wBAAwB;;AAErC,OAAO,QAAQ,uBAAmB;AAElC,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,oBAAgB;AA2ChE;;;;;;;;;GASG;AACH,MAAM,OAAgB,YAAa,SAAQ,gBAAgB;IA4BzD;;;;;;;;OAQG;IACH,YAAY,EACV,MAAM,GAAG,OAAO,EAChB,iBAAiB,GAAG,GAAG,EACvB,aAAa,GAAG,EAAE,MACK,EAAE;QACzB,KAAK,EAAE,CAAC;QA3BV;;;WAGG;QACH,wCAAwB;QAExB;;;;WAIG;QACH,gDAAgC;QAkB9B,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;QAEnB,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;QAExC,gBAAgB;QAChB,IAAI,CAAC,MAAM,GAAG;YACZ,GAAG,YAAY,CAAC,aAAa;SAC9B,CAAC;QAEF,eAAe;QACf,uBAAA,IAAI,iCAAoB,IAAI,MAAA,CAAC;QAC7B,uBAAA,IAAI,yBAAY,IAAI,MAAA,CAAC;QAErB,gEAAgE;QAChE,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEvC,gDAAgD;QAChD,EAAE;QACF,uEAAuE;QACvE,cAAc;QACd,MAAM,SAAS,GAAG,IAAI,aAAa,EAAE,CAAC;QACtC,aAAa,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,sBAAsB;IACtB,oBAAoB;IACpB,sBAAsB;IAEtB,IAAI,OAAO;QACT,OAAO,uBAAA,IAAI,6BAAS,CAAC;IACvB,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,uBAAA,IAAI,qCAAiB,CAAC;IAC/B,CAAC;IAED,sBAAsB;IACtB,iBAAiB;IACjB,sBAAsB;IAEtB;;;;OAIG;IACH,WAAW;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;IACjC,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,OAAO,CAAO,IAAsB;QACxC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7D,MAAM,SAAS,CAAC,cAAc,CAAC;gBAC7B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,kBAAkB,EAAE;gBAC7C,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;QACL,CAAC;QAED,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAEhC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtD,MAAM,SAAS,CAAC,cAAc,CAAC;gBAC7B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,oBAAoB,EAAE;gBAC/C,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;QACL,CAAC;QAED,IACE,MAAM,KAAK,SAAS;YACpB,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YACtB,CAAC,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,CAAC,EAC/C,CAAC;YACD,MAAM,SAAS,CAAC,cAAc,CAAC;gBAC7B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,oBAAoB,EAAE;gBAC/C,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GACX,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI;YACrC,CAAC,CAAC;gBACE,MAAM;aACP;YACH,CAAC,CAAC;gBACE,MAAM;gBACN,MAAM;aACP,CAAC;QAER,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,sBAAsB;IACtB,kBAAkB;IAClB,sBAAsB;IAEtB;;;;;;;;;;;;;;;;;OAiBG;IACO,gBAAgB,CAAC,YAM1B;QACC,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,GAClE,YAAY,CAAC;YAEf,mBAAmB;YACnB,IAAI,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;YAC9C,IAAI,CAAC,mBAAmB,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;YACnE,IAAI,CAAC,yBAAyB,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;YACzD,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC;QAED,uEAAuE;QACvE,aAAa;QACb,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC5B,CAAC;IAED;;;;;;;OAOG;IACO,WAAW,CACnB,OAAgE,EAChE,QAAkC;QAElC,IAAI,eAAe,GAAG,QAAQ,CAAC;QAE/B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBACrB,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;YAC1B,CAAC;YAED,IACE,OAAO,CAAC,MAAM,KAAK,cAAc;gBACjC,OAAO,CAAC,MAAM,KAAK,qBAAqB,EACxC,CAAC;gBACD,2BAA2B;gBAC3B,eAAe,GAAG,CAChB,KAAY,EACZ,QAAkC,EAClC,EAAE;oBACF,IAAI,CAAC,sBAAsB,CACzB,QAAQ,CAAC,MAAM,IAAI,EAAE,EACrB,OAAO,CAAC,MAAM,KAAK,cAAc,CAClC,CAAC;oBACF,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;gBAC5B,CAAC,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAyB,EAAE,eAAe,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAA2B,EAAE,eAAe,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;;;;OAQG;IACO,cAAc,CAAC,EACvB,OAAO,EACP,WAAW,GAIZ;QACC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,WAAW,EAAE,CAAC;YAC5C,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED;;;;;;;;;;OAUG;IACO,iBAAiB,CAAC,aAAsB,EAAE,YAAqB;QACvE,IACE,IAAI,CAAC,MAAM,CAAC,WAAW;YACvB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,yBAAyB,IAAI,CAAC,aAAa,CAAC,EAC1D,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC;YAEhC,IAAI,KAAK,CAAC;YACV,IAAI,aAAa,EAAE,CAAC;gBAClB,KAAK,GAAG,IAAI,YAAY,CACtB,IAAI,EAAE,kBAAkB;gBACxB,YAAY,IAAI,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,CAC/C,CAAC;gBACF,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACzB,CAAC;iBAAM,CAAC;gBACN,KAAK,GAAG,IAAI,YAAY,CACtB,IAAI,EAAE,iBAAiB;gBACvB,YAAY,IAAI,QAAQ,CAAC,MAAM,CAAC,uBAAuB,EAAE,CAC1D,CAAC;gBACF,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACvB,uBAAA,IAAI,yBAAY,IAAI,MAAA,CAAC;gBACrB,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC;gBAC5B,uBAAA,IAAI,iCAAoB,IAAI,MAAA,CAAC;gBAC7B,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,KAAK,CAAC;gBAC/B,IAAI,CAAC,MAAM,CAAC,yBAAyB,GAAG,IAAI,CAAC;YAC/C,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;OAYG;IACO,mBAAmB,CAAC,EAC5B,OAAO,EACP,WAAW,MAOG,EAAE;QAChB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,oBAAoB,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;YACrE,OAAO;QACT,CAAC;QAED,IAAI,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QAE9C,IAAI,OAAO,KAAK,uBAAA,IAAI,6BAAS,EAAE,CAAC;YAC9B,uBAAA,IAAI,yBAAY,OAAO,MAAA,CAAC;YACxB,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;gBAC5B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,uBAAA,IAAI,6BAAS,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACO,sBAAsB,CAC9B,QAAmB,EACnB,aAAa,GAAG,KAAK;QAErB,IAAI,SAAS,GAAG,QAAQ,CAAC;QAEzB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,KAAK,CACb,wEAAwE,EACxE,QAAQ,CACT,CAAC;YACF,SAAS,GAAG,EAAE,CAAC;QACjB,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAChC,IAAI,CAAC,IAAI,CAAC,KAAK,CACb,gEAAgE,EAChE,QAAQ,CACT,CAAC;gBACF,SAAS,GAAG,EAAE,CAAC;gBACf,MAAM;YACR,CAAC;QACH,CAAC;QAED,wEAAwE;QACxE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC;YAC7C,sEAAsE;YACtE,UAAU;YACV,IAAI,aAAa,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;gBACnD,IAAI,CAAC,IAAI,CAAC,KAAK,CACb,iFAAiF,EACjF,SAAS,CACV,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,SAAqB,CAAC;YAE7C,yBAAyB;YACzB,IAAI,uBAAA,IAAI,qCAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3C,uBAAA,IAAI,iCAAqB,SAAS,CAAC,CAAC,CAAY,IAAI,IAAI,MAAA,CAAC;YAC3D,CAAC;YAED,4DAA4D;YAC5D,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;gBAC5B,MAAM,aAAa,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;gBACrC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACO,yBAAyB,CAAC,EAClC,QAAQ,EACR,UAAU,MACuC,EAAE;QACnD,IAAI,OAAO,UAAU,KAAK,SAAS,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,KAAK,CACb,0EAA0E,CAC3E,CAAC;YACF,OAAO;QACT,CAAC;QAED,IAAI,UAAU,KAAK,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAC1C,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,UAAU,CAAC;YACpC,IAAI,CAAC,sBAAsB,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;;;AA1agB,0BAAa,GAAsB;IAClD,QAAQ,EAAE,IAAI;IACd,WAAW,EAAE,KAAK;IAClB,UAAU,EAAE,KAAK;IACjB,WAAW,EAAE,KAAK;IAClB,yBAAyB,EAAE,KAAK;CACjC,AAN6B,CAM5B","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { JsonRpcEngine } from '@metamask/json-rpc-engine';\nimport { rpcErrors, JsonRpcError } from '@metamask/rpc-errors';\nimport SafeEventEmitter from '@metamask/safe-event-emitter';\nimport type {\n JsonRpcRequest,\n JsonRpcId,\n JsonRpcVersion2,\n JsonRpcSuccess,\n JsonRpcParams,\n Json,\n} from '@metamask/utils';\nimport dequal from 'fast-deep-equal';\n\nimport messages from './messages';\nimport type { ConsoleLike, Maybe } from './utils';\nimport { getRpcPromiseCallback, isValidChainId } from './utils';\n\nexport type UnvalidatedJsonRpcRequest = {\n id?: JsonRpcId;\n jsonrpc?: JsonRpcVersion2;\n method: string;\n params?: unknown;\n};\n\nexport type BaseProviderOptions = {\n /**\n * The logging API to use.\n */\n logger?: ConsoleLike;\n\n /**\n * The maximum number of event listeners.\n */\n maxEventListeners?: number;\n\n /**\n * `@metamask/json-rpc-engine` middleware. The middleware will be inserted in the given\n * order immediately after engine initialization.\n */\n rpcMiddleware?: JsonRpcMiddleware[];\n};\n\nexport type RequestArguments = {\n /** The RPC method to request. */\n method: string;\n\n /** The params of the RPC method, if any. */\n params?: unknown[] | Record;\n};\n\nexport type BaseProviderState = {\n accounts: null | string[];\n isConnected: boolean;\n isUnlocked: boolean;\n initialized: boolean;\n isPermanentlyDisconnected: boolean;\n};\n\n/**\n * An abstract class implementing the EIP-1193 interface. Implementers must:\n *\n * 1. At initialization, push a middleware to the internal `_rpcEngine` that\n * hands off requests to the server and receives responses in return.\n * 2. At initialization, retrieve initial state and call\n * {@link BaseProvider._initializeState} **once**.\n * 3. Ensure that the provider's state is synchronized with the wallet.\n * 4. Ensure that notifications are received and emitted as appropriate.\n */\nexport abstract class BaseProvider extends SafeEventEmitter {\n protected readonly _log: ConsoleLike;\n\n protected _state: BaseProviderState;\n\n protected _rpcEngine: JsonRpcEngine;\n\n protected static _defaultState: BaseProviderState = {\n accounts: null,\n isConnected: false,\n isUnlocked: false,\n initialized: false,\n isPermanentlyDisconnected: false,\n };\n\n /**\n * The chain ID of the currently connected Ethereum chain.\n * See [chainId.network]{@link https://chainid.network} for more information.\n */\n #chainId: string | null;\n\n /**\n * The user's currently selected Ethereum address.\n * If null, MetaMask is either locked or the user has not permitted any\n * addresses to be viewed.\n */\n #selectedAddress: string | null;\n\n /**\n * Create a new instance of the provider.\n *\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.rpcMiddleware - The RPC middleware stack. Default: [].\n */\n constructor({\n logger = console,\n maxEventListeners = 100,\n rpcMiddleware = [],\n }: BaseProviderOptions = {}) {\n super();\n\n this._log = logger;\n\n this.setMaxListeners(maxEventListeners);\n\n // Private state\n this._state = {\n ...BaseProvider._defaultState,\n };\n\n // Public state\n this.#selectedAddress = null;\n this.#chainId = null;\n\n // Bind functions to prevent consumers from making unbound calls\n this._handleAccountsChanged = this._handleAccountsChanged.bind(this);\n this._handleConnect = this._handleConnect.bind(this);\n this._handleChainChanged = this._handleChainChanged.bind(this);\n this._handleDisconnect = this._handleDisconnect.bind(this);\n this._handleUnlockStateChanged = this._handleUnlockStateChanged.bind(this);\n this._rpcRequest = this._rpcRequest.bind(this);\n this.request = this.request.bind(this);\n\n // Handle RPC requests via dapp-side RPC engine.\n //\n // ATTN: Implementers must push a middleware that hands off requests to\n // the server.\n const rpcEngine = new JsonRpcEngine();\n rpcMiddleware.forEach((middleware) => rpcEngine.push(middleware));\n this._rpcEngine = rpcEngine;\n }\n\n //====================\n // Public Properties\n //====================\n\n get chainId(): string | null {\n return this.#chainId;\n }\n\n get selectedAddress(): string | null {\n return this.#selectedAddress;\n }\n\n //====================\n // Public Methods\n //====================\n\n /**\n * Returns whether the provider can process RPC requests.\n *\n * @returns Whether the provider can process RPC requests.\n */\n isConnected(): boolean {\n return this._state.isConnected;\n }\n\n /**\n * Submits an RPC request for the given method, with the given params.\n * Resolves with the result of the method call, or rejects on error.\n *\n * @param args - The RPC request arguments.\n * @param args.method - The RPC method name.\n * @param args.params - The parameters for the RPC method.\n * @returns A Promise that resolves with the result of the RPC method,\n * or rejects if an error is encountered.\n */\n async request(args: RequestArguments): Promise> {\n if (!args || typeof args !== 'object' || Array.isArray(args)) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestArgs(),\n data: args,\n });\n }\n\n const { method, params } = args;\n\n if (typeof method !== 'string' || method.length === 0) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestMethod(),\n data: args,\n });\n }\n\n if (\n params !== undefined &&\n !Array.isArray(params) &&\n (typeof params !== 'object' || params === null)\n ) {\n throw rpcErrors.invalidRequest({\n message: messages.errors.invalidRequestParams(),\n data: args,\n });\n }\n\n const payload =\n params === undefined || params === null\n ? {\n method,\n }\n : {\n method,\n params,\n };\n\n return new Promise((resolve, reject) => {\n this._rpcRequest(payload, getRpcPromiseCallback(resolve, reject));\n });\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * MUST be called by child classes.\n *\n * Sets initial state if provided and marks this provider as initialized.\n * Throws if called more than once.\n *\n * Permits the `networkVersion` field in the parameter object for\n * compatibility with child classes that use this value.\n *\n * @param initialState - The provider's initial state.\n * @param initialState.accounts - The user's accounts.\n * @param initialState.chainId - The chain ID.\n * @param initialState.isUnlocked - Whether the user has unlocked MetaMask.\n * @param initialState.networkVersion - The network version.\n * @param initialState.isConnected - Whether the network is available.\n * @fires BaseProvider#_initialized - If `initialState` is defined.\n * @fires BaseProvider#connect - If `initialState` is defined.\n */\n protected _initializeState(initialState?: {\n accounts: string[];\n chainId: string;\n isUnlocked: boolean;\n networkVersion?: string;\n isConnected?: boolean;\n }) {\n if (this._state.initialized) {\n throw new Error('Provider already initialized.');\n }\n\n if (initialState) {\n const { accounts, chainId, isUnlocked, networkVersion, isConnected } =\n initialState;\n\n // EIP-1193 connect\n this._handleConnect({ chainId, isConnected });\n this._handleChainChanged({ chainId, networkVersion, isConnected });\n this._handleUnlockStateChanged({ accounts, isUnlocked });\n this._handleAccountsChanged(accounts);\n }\n\n // Mark provider as initialized regardless of whether initial state was\n // retrieved.\n this._state.initialized = true;\n this.emit('_initialized');\n }\n\n /**\n * Internal RPC method. Forwards requests to background via the RPC engine.\n * Also remap ids inbound and outbound.\n *\n * @param payload - The RPC request object.\n * @param callback - The consumer's callback.\n * @returns The result of the RPC request.\n */\n protected _rpcRequest(\n payload: UnvalidatedJsonRpcRequest | UnvalidatedJsonRpcRequest[],\n callback: (...args: any[]) => void,\n ) {\n let callbackWrapper = callback;\n\n if (!Array.isArray(payload)) {\n if (!payload.jsonrpc) {\n payload.jsonrpc = '2.0';\n }\n\n if (\n payload.method === 'eth_accounts' ||\n payload.method === 'eth_requestAccounts'\n ) {\n // handle accounts changing\n callbackWrapper = (\n error: Error,\n response: JsonRpcSuccess,\n ) => {\n this._handleAccountsChanged(\n response.result ?? [],\n payload.method === 'eth_accounts',\n );\n callback(error, response);\n };\n }\n return this._rpcEngine.handle(payload as JsonRpcRequest, callbackWrapper);\n }\n return this._rpcEngine.handle(payload as JsonRpcRequest[], callbackWrapper);\n }\n\n /**\n * When the provider becomes connected, updates internal state and emits\n * required events. Idempotent.\n *\n * @param networkInfo - The options object.\n * @param networkInfo.chainId - The ID of the newly connected chain.\n * @param networkInfo.isConnected - Whether the network is available.\n * @fires MetaMaskInpageProvider#connect\n */\n protected _handleConnect({\n chainId,\n isConnected,\n }: {\n chainId: string;\n isConnected?: boolean | undefined;\n }) {\n if (!this._state.isConnected && isConnected) {\n this._state.isConnected = true;\n this.emit('connect', { chainId });\n this._log.debug(messages.info.connected(chainId));\n }\n }\n\n /**\n * When the provider becomes disconnected, updates internal state and emits\n * required events. Idempotent with respect to the isRecoverable parameter.\n *\n * Error codes per the CloseEvent status codes as required by EIP-1193:\n * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes.\n *\n * @param isRecoverable - Whether the disconnection is recoverable.\n * @param errorMessage - A custom error message.\n * @fires BaseProvider#disconnect - If the disconnection is not recoverable.\n */\n protected _handleDisconnect(isRecoverable: boolean, errorMessage?: string) {\n if (\n this._state.isConnected ||\n (!this._state.isPermanentlyDisconnected && !isRecoverable)\n ) {\n this._state.isConnected = false;\n\n let error;\n if (isRecoverable) {\n error = new JsonRpcError(\n 1013, // Try again later\n errorMessage ?? messages.errors.disconnected(),\n );\n this._log.debug(error);\n } else {\n error = new JsonRpcError(\n 1011, // Internal error\n errorMessage ?? messages.errors.permanentlyDisconnected(),\n );\n this._log.error(error);\n this.#chainId = null;\n this._state.accounts = null;\n this.#selectedAddress = null;\n this._state.isUnlocked = false;\n this._state.isPermanentlyDisconnected = true;\n }\n\n this.emit('disconnect', error);\n }\n }\n\n /**\n * Upon receipt of a new `chainId`, emits the corresponding event and sets\n * and sets relevant public state. Does nothing if the given `chainId` is\n * equivalent to the existing value.\n *\n * Permits the `networkVersion` field in the parameter object for\n * compatibility with child classes that use this value.\n *\n * @fires BaseProvider#chainChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n * @param networkInfo.isConnected - Whether the network is available.\n */\n protected _handleChainChanged({\n chainId,\n isConnected,\n }:\n | {\n chainId?: string;\n networkVersion?: string | undefined;\n isConnected?: boolean | undefined;\n }\n | undefined = {}) {\n if (!isValidChainId(chainId)) {\n this._log.error(messages.errors.invalidNetworkParams(), { chainId });\n return;\n }\n\n this._handleConnect({ chainId, isConnected });\n\n if (chainId !== this.#chainId) {\n this.#chainId = chainId;\n if (this._state.initialized) {\n this.emit('chainChanged', this.#chainId);\n }\n }\n }\n\n /**\n * Called when accounts may have changed. Diffs the new accounts value with\n * the current one, updates all state as necessary, and emits the\n * accountsChanged event.\n *\n * @param accounts - The new accounts value.\n * @param isEthAccounts - Whether the accounts value was returned by\n * a call to eth_accounts.\n */\n protected _handleAccountsChanged(\n accounts: unknown[],\n isEthAccounts = false,\n ): void {\n let _accounts = accounts;\n\n if (!Array.isArray(accounts)) {\n this._log.error(\n 'MetaMask: Received invalid accounts parameter. Please report this bug.',\n accounts,\n );\n _accounts = [];\n }\n\n for (const account of accounts) {\n if (typeof account !== 'string') {\n this._log.error(\n 'MetaMask: Received non-string account. Please report this bug.',\n accounts,\n );\n _accounts = [];\n break;\n }\n }\n\n // emit accountsChanged if anything about the accounts array has changed\n if (!dequal(this._state.accounts, _accounts)) {\n // we should always have the correct accounts even before eth_accounts\n // returns\n if (isEthAccounts && this._state.accounts !== null) {\n this._log.error(\n `MetaMask: 'eth_accounts' unexpectedly updated accounts. Please report this bug.`,\n _accounts,\n );\n }\n\n this._state.accounts = _accounts as string[];\n\n // handle selectedAddress\n if (this.#selectedAddress !== _accounts[0]) {\n this.#selectedAddress = (_accounts[0] as string) || null;\n }\n\n // finally, after all state has been updated, emit the event\n if (this._state.initialized) {\n const _nextAccounts = [..._accounts];\n this.emit('accountsChanged', _nextAccounts);\n }\n }\n }\n\n /**\n * Upon receipt of a new isUnlocked state, sets relevant public state.\n * Calls the accounts changed handler with the received accounts, or an empty\n * array.\n *\n * Does nothing if the received value is equal to the existing value.\n * There are no lock/unlock events.\n *\n * @param opts - Options bag.\n * @param opts.accounts - The exposed accounts, if any.\n * @param opts.isUnlocked - The latest isUnlocked value.\n */\n protected _handleUnlockStateChanged({\n accounts,\n isUnlocked,\n }: { accounts?: string[]; isUnlocked?: boolean } = {}) {\n if (typeof isUnlocked !== 'boolean') {\n this._log.error(\n 'MetaMask: Received invalid isUnlocked parameter. Please report this bug.',\n );\n return;\n }\n\n if (isUnlocked !== this._state.isUnlocked) {\n this._state.isUnlocked = isUnlocked;\n this._handleAccountsChanged(accounts ?? []);\n }\n }\n}\n"]} -\ No newline at end of file -diff --git a/dist/MetaMaskInpageProvider.cjs b/dist/MetaMaskInpageProvider.cjs -index 048f5ebc5bcf239682b44faae7dd5bf963db628f..63a83d4c25b54c087891f14944c8cf41bcd8e629 100644 ---- a/dist/MetaMaskInpageProvider.cjs -+++ b/dist/MetaMaskInpageProvider.cjs -@@ -307,21 +307,29 @@ class MetaMaskInpageProvider extends StreamProvider_1.AbstractStreamProvider { - }); - } - /** -- * Upon receipt of a new chainId and networkVersion, emits corresponding -- * events and sets relevant public state. Does nothing if neither the chainId -- * nor the networkVersion are different from existing values. -+ * Upon receipt of a new chainId, networkVersion, and isConnected value -+ * emits corresponding events and sets relevant public state. We interpret -+ * a `networkVersion` with the value of `loading` to be null. The `isConnected` -+ * value determines if a `connect` or recoverable `disconnect` has occurred. -+ * Child classes that use the `networkVersion` for other purposes must implement -+ * additional handling therefore. - * - * @fires MetamaskInpageProvider#networkChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. - * @param networkInfo.networkVersion - The latest network ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- _handleChainChanged({ chainId, networkVersion, } = {}) { -- // This will validate the params and disconnect the provider if the -- // networkVersion is 'loading'. -- super._handleChainChanged({ chainId, networkVersion }); -- if (this._state.isConnected && networkVersion !== __classPrivateFieldGet(this, _MetaMaskInpageProvider_networkVersion, "f")) { -- __classPrivateFieldSet(this, _MetaMaskInpageProvider_networkVersion, networkVersion, "f"); -+ _handleChainChanged({ chainId, networkVersion, isConnected, } = {}) { -+ super._handleChainChanged({ chainId, networkVersion, isConnected }); -+ // The wallet will send a value of `loading` for `networkVersion` when it intends -+ // to communicate that this value cannot be resolved and should be intepreted as null. -+ // The wallet cannot directly send a null value for `networkVersion` because this -+ // would be a breaking change for existing dapps that use their own embedded MetaMask provider -+ // that expect this value to always be a integer string or the value 'loading'. -+ const targetNetworkVersion = networkVersion === 'loading' ? null : networkVersion; -+ if (targetNetworkVersion !== __classPrivateFieldGet(this, _MetaMaskInpageProvider_networkVersion, "f")) { -+ __classPrivateFieldSet(this, _MetaMaskInpageProvider_networkVersion, targetNetworkVersion, "f"); - if (this._state.initialized) { - this.emit('networkChanged', __classPrivateFieldGet(this, _MetaMaskInpageProvider_networkVersion, "f")); - } -diff --git a/dist/MetaMaskInpageProvider.cjs.map b/dist/MetaMaskInpageProvider.cjs.map -index cf575a44348b1df1ac695bf18281a5b956f0e003..73dfb0006819b892eb2e4d0be06a44710e3cbe6a 100644 ---- a/dist/MetaMaskInpageProvider.cjs.map -+++ b/dist/MetaMaskInpageProvider.cjs.map -@@ -1 +1 @@ --{"version":3,"file":"MetaMaskInpageProvider.cjs","sourceRoot":"","sources":["../src/MetaMaskInpageProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import { rpcErrors } from '@metamask/rpc-errors';\nimport type { Json, JsonRpcRequest, JsonRpcResponse } from '@metamask/utils';\nimport type { Duplex } from 'readable-stream';\n\nimport type { UnvalidatedJsonRpcRequest } from './BaseProvider';\nimport messages from './messages';\nimport { sendSiteMetadata } from './siteMetadata';\nimport type { StreamProviderOptions } from './StreamProvider';\nimport { AbstractStreamProvider } from './StreamProvider';\nimport {\n EMITTED_NOTIFICATIONS,\n getDefaultExternalMiddleware,\n getRpcPromiseCallback,\n NOOP,\n} from './utils';\n\nexport type SendSyncJsonRpcRequest = {\n method:\n | 'eth_accounts'\n | 'eth_coinbase'\n | 'eth_uninstallFilter'\n | 'net_version';\n} & JsonRpcRequest;\n\ntype WarningEventName = keyof SentWarningsState['events'];\n\nexport type MetaMaskInpageProviderOptions = {\n /**\n * Whether the provider should send page metadata.\n */\n shouldSendMetadata?: boolean;\n} & Partial>;\n\ntype SentWarningsState = {\n // methods\n enable: boolean;\n experimentalMethods: boolean;\n send: boolean;\n // events\n events: {\n close: boolean;\n data: boolean;\n networkChanged: boolean;\n notification: boolean;\n };\n};\n\n/**\n * The name of the stream consumed by {@link MetaMaskInpageProvider}.\n */\nexport const MetaMaskInpageProviderStreamName = 'metamask-provider';\n\nexport class MetaMaskInpageProvider extends AbstractStreamProvider {\n protected _sentWarnings: SentWarningsState = {\n // methods\n enable: false,\n experimentalMethods: false,\n send: false,\n // events\n events: {\n close: false,\n data: false,\n networkChanged: false,\n notification: false,\n },\n };\n\n /**\n * Experimental methods can be found here.\n */\n public readonly _metamask: ReturnType<\n MetaMaskInpageProvider['_getExperimentalApi']\n >;\n\n #networkVersion: string | null;\n\n /**\n * Indicating that this provider is a MetaMask provider.\n */\n public readonly isMetaMask: true;\n\n /**\n * Creates a new `MetaMaskInpageProvider`.\n *\n * @param connectionStream - A Node.js duplex stream.\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.shouldSendMetadata - Whether the provider should\n * send page metadata. Default: `true`.\n */\n constructor(\n connectionStream: Duplex,\n {\n logger = console,\n maxEventListeners = 100,\n shouldSendMetadata,\n }: MetaMaskInpageProviderOptions = {},\n ) {\n super(connectionStream, {\n logger,\n maxEventListeners,\n rpcMiddleware: getDefaultExternalMiddleware(logger),\n });\n\n // We shouldn't perform asynchronous work in the constructor, but at one\n // point we started doing so, and changing this class isn't worth it at\n // the time of writing.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this._initializeStateAsync();\n\n this.#networkVersion = null;\n this.isMetaMask = true;\n\n this._sendSync = this._sendSync.bind(this);\n this.enable = this.enable.bind(this);\n this.send = this.send.bind(this);\n this.sendAsync = this.sendAsync.bind(this);\n this._warnOfDeprecation = this._warnOfDeprecation.bind(this);\n\n this._metamask = this._getExperimentalApi();\n\n // handle JSON-RPC notifications\n this._jsonRpcConnection.events.on('notification', (payload) => {\n const { method } = payload;\n if (EMITTED_NOTIFICATIONS.includes(method)) {\n // deprecated\n // emitted here because that was the original order\n this.emit('data', payload);\n // deprecated\n this.emit('notification', payload.params.result);\n }\n });\n\n // send website metadata\n if (shouldSendMetadata) {\n if (document.readyState === 'complete') {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n sendSiteMetadata(this._rpcEngine, this._log);\n } else {\n const domContentLoadedHandler = () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n sendSiteMetadata(this._rpcEngine, this._log);\n window.removeEventListener(\n 'DOMContentLoaded',\n domContentLoadedHandler,\n );\n };\n window.addEventListener('DOMContentLoaded', domContentLoadedHandler);\n }\n }\n }\n\n //====================\n // Read-only Properties\n //====================\n\n get chainId(): string | null {\n return super.chainId;\n }\n\n get networkVersion(): string | null {\n return this.#networkVersion;\n }\n\n get selectedAddress(): string | null {\n return super.selectedAddress;\n }\n\n //====================\n // Public Methods\n //====================\n\n /**\n * Submits an RPC request per the given JSON-RPC request object.\n *\n * @param payload - The RPC request object.\n * @param callback - The callback function.\n */\n sendAsync(\n payload: JsonRpcRequest,\n callback: (error: Error | null, result?: JsonRpcResponse) => void,\n ): void {\n this._rpcRequest(payload, callback);\n }\n\n /**\n * We override the following event methods so that we can warn consumers\n * about deprecated events:\n * `addListener`, `on`, `once`, `prependListener`, `prependOnceListener`.\n */\n\n addListener(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.addListener(eventName, listener);\n }\n\n on(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.on(eventName, listener);\n }\n\n once(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.once(eventName, listener);\n }\n\n prependListener(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.prependListener(eventName, listener);\n }\n\n prependOnceListener(\n eventName: string,\n listener: (...args: unknown[]) => void,\n ) {\n this._warnOfDeprecation(eventName);\n return super.prependOnceListener(eventName, listener);\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * When the provider becomes disconnected, updates internal state and emits\n * required events. Idempotent with respect to the isRecoverable parameter.\n *\n * Error codes per the CloseEvent status codes as required by EIP-1193:\n * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes.\n *\n * @param isRecoverable - Whether the disconnection is recoverable.\n * @param errorMessage - A custom error message.\n * @fires BaseProvider#disconnect - If the disconnection is not recoverable.\n */\n protected _handleDisconnect(isRecoverable: boolean, errorMessage?: string) {\n super._handleDisconnect(isRecoverable, errorMessage);\n if (this.#networkVersion && !isRecoverable) {\n this.#networkVersion = null;\n }\n }\n\n /**\n * Warns of deprecation for the given event, if applicable.\n *\n * @param eventName - The name of the event.\n */\n protected _warnOfDeprecation(eventName: string): void {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare\n if (this._sentWarnings?.events[eventName as WarningEventName] === false) {\n this._log.warn(messages.warnings.events[eventName as WarningEventName]);\n this._sentWarnings.events[eventName as WarningEventName] = true;\n }\n }\n\n //====================\n // Deprecated Methods\n //====================\n\n /**\n * Equivalent to: `ethereum.request('eth_requestAccounts')`.\n *\n * @deprecated Use request({ method: 'eth_requestAccounts' }) instead.\n * @returns A promise that resolves to an array of addresses.\n */\n async enable(): Promise {\n if (!this._sentWarnings.enable) {\n this._log.warn(messages.warnings.enableDeprecation);\n this._sentWarnings.enable = true;\n }\n\n return new Promise((resolve, reject) => {\n try {\n this._rpcRequest(\n { method: 'eth_requestAccounts', params: [] },\n getRpcPromiseCallback(resolve, reject),\n );\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Submits an RPC request for the given method, with the given params.\n *\n * @deprecated Use \"request\" instead.\n * @param method - The method to request.\n * @param params - Any params for the method.\n * @returns A Promise that resolves with the JSON-RPC response object for the\n * request.\n */\n send(\n method: string,\n params?: Type[],\n ): Promise>;\n\n /**\n * Submits an RPC request per the given JSON-RPC request object.\n *\n * @deprecated Use \"request\" instead.\n * @param payload - A JSON-RPC request object.\n * @param callback - An error-first callback that will receive the JSON-RPC\n * response object.\n */\n send(\n payload: JsonRpcRequest,\n callback: (error: Error | null, result?: JsonRpcResponse) => void,\n ): void;\n\n /**\n * Accepts a JSON-RPC request object, and synchronously returns the cached result\n * for the given method. Only supports 4 specific RPC methods.\n *\n * @deprecated Use \"request\" instead.\n * @param payload - A JSON-RPC request object.\n * @returns A JSON-RPC response object.\n */\n send(\n payload: SendSyncJsonRpcRequest,\n ): JsonRpcResponse;\n\n // eslint-disable-next-line @typescript-eslint/promise-function-async\n send(methodOrPayload: unknown, callbackOrArgs?: unknown): unknown {\n if (!this._sentWarnings.send) {\n this._log.warn(messages.warnings.sendDeprecation);\n this._sentWarnings.send = true;\n }\n\n if (\n typeof methodOrPayload === 'string' &&\n (!callbackOrArgs || Array.isArray(callbackOrArgs))\n ) {\n return new Promise((resolve, reject) => {\n try {\n this._rpcRequest(\n { method: methodOrPayload, params: callbackOrArgs },\n getRpcPromiseCallback(resolve, reject, false),\n );\n } catch (error) {\n reject(error);\n }\n });\n } else if (\n methodOrPayload &&\n typeof methodOrPayload === 'object' &&\n typeof callbackOrArgs === 'function'\n ) {\n return this._rpcRequest(\n methodOrPayload as JsonRpcRequest,\n callbackOrArgs as (...args: unknown[]) => void,\n );\n }\n return this._sendSync(methodOrPayload as SendSyncJsonRpcRequest);\n }\n\n /**\n * Internal backwards compatibility method, used in send.\n *\n * @param payload - A JSON-RPC request object.\n * @returns A JSON-RPC response object.\n * @deprecated\n */\n protected _sendSync(payload: SendSyncJsonRpcRequest) {\n let result;\n switch (payload.method) {\n case 'eth_accounts':\n result = this.selectedAddress ? [this.selectedAddress] : [];\n break;\n\n case 'eth_coinbase':\n result = this.selectedAddress ?? null;\n break;\n\n case 'eth_uninstallFilter':\n this._rpcRequest(payload, NOOP);\n result = true;\n break;\n\n case 'net_version':\n result = this.#networkVersion ?? null;\n break;\n\n default:\n throw new Error(messages.errors.unsupportedSync(payload.method));\n }\n\n return {\n id: payload.id,\n jsonrpc: payload.jsonrpc,\n result,\n };\n }\n\n /**\n * Constructor helper.\n *\n * Gets the experimental _metamask API as Proxy, so that we can warn consumers\n * about its experimental nature.\n *\n * @returns The experimental _metamask API.\n */\n protected _getExperimentalApi() {\n return new Proxy(\n {\n /**\n * Determines if MetaMask is unlocked by the user.\n *\n * @returns Promise resolving to true if MetaMask is currently unlocked.\n */\n isUnlocked: async () => {\n if (!this._state.initialized) {\n await new Promise((resolve) => {\n this.on('_initialized', () => resolve());\n });\n }\n return this._state.isUnlocked;\n },\n\n /**\n * Make a batch RPC request.\n *\n * @param requests - The RPC requests to make.\n */\n requestBatch: async (requests: UnvalidatedJsonRpcRequest[]) => {\n if (!Array.isArray(requests)) {\n throw rpcErrors.invalidRequest({\n message:\n 'Batch requests must be made with an array of request objects.',\n data: requests,\n });\n }\n\n return new Promise((resolve, reject) => {\n this._rpcRequest(requests, getRpcPromiseCallback(resolve, reject));\n });\n },\n },\n {\n get: (obj, prop, ...args) => {\n if (!this._sentWarnings.experimentalMethods) {\n this._log.warn(messages.warnings.experimentalMethods);\n this._sentWarnings.experimentalMethods = true;\n }\n return Reflect.get(obj, prop, ...args);\n },\n },\n );\n }\n\n /**\n * Upon receipt of a new chainId and networkVersion, emits corresponding\n * events and sets relevant public state. Does nothing if neither the chainId\n * nor the networkVersion are different from existing values.\n *\n * @fires MetamaskInpageProvider#networkChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n * @param networkInfo.networkVersion - The latest network ID.\n */\n protected _handleChainChanged({\n chainId,\n networkVersion,\n }: { chainId?: string; networkVersion?: string } = {}) {\n // This will validate the params and disconnect the provider if the\n // networkVersion is 'loading'.\n super._handleChainChanged({ chainId, networkVersion });\n\n if (this._state.isConnected && networkVersion !== this.#networkVersion) {\n this.#networkVersion = networkVersion as string;\n if (this._state.initialized) {\n this.emit('networkChanged', this.#networkVersion);\n }\n }\n }\n}\n"]} -\ No newline at end of file -+{"version":3,"file":"MetaMaskInpageProvider.cjs","sourceRoot":"","sources":["../src/MetaMaskInpageProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import { rpcErrors } from '@metamask/rpc-errors';\nimport type { Json, JsonRpcRequest, JsonRpcResponse } from '@metamask/utils';\nimport type { Duplex } from 'readable-stream';\n\nimport type { UnvalidatedJsonRpcRequest } from './BaseProvider';\nimport messages from './messages';\nimport { sendSiteMetadata } from './siteMetadata';\nimport type { StreamProviderOptions } from './StreamProvider';\nimport { AbstractStreamProvider } from './StreamProvider';\nimport {\n EMITTED_NOTIFICATIONS,\n getDefaultExternalMiddleware,\n getRpcPromiseCallback,\n NOOP,\n} from './utils';\n\nexport type SendSyncJsonRpcRequest = {\n method:\n | 'eth_accounts'\n | 'eth_coinbase'\n | 'eth_uninstallFilter'\n | 'net_version';\n} & JsonRpcRequest;\n\ntype WarningEventName = keyof SentWarningsState['events'];\n\nexport type MetaMaskInpageProviderOptions = {\n /**\n * Whether the provider should send page metadata.\n */\n shouldSendMetadata?: boolean;\n} & Partial>;\n\ntype SentWarningsState = {\n // methods\n enable: boolean;\n experimentalMethods: boolean;\n send: boolean;\n // events\n events: {\n close: boolean;\n data: boolean;\n networkChanged: boolean;\n notification: boolean;\n };\n};\n\n/**\n * The name of the stream consumed by {@link MetaMaskInpageProvider}.\n */\nexport const MetaMaskInpageProviderStreamName = 'metamask-provider';\n\nexport class MetaMaskInpageProvider extends AbstractStreamProvider {\n protected _sentWarnings: SentWarningsState = {\n // methods\n enable: false,\n experimentalMethods: false,\n send: false,\n // events\n events: {\n close: false,\n data: false,\n networkChanged: false,\n notification: false,\n },\n };\n\n /**\n * Experimental methods can be found here.\n */\n public readonly _metamask: ReturnType<\n MetaMaskInpageProvider['_getExperimentalApi']\n >;\n\n #networkVersion: string | null;\n\n /**\n * Indicating that this provider is a MetaMask provider.\n */\n public readonly isMetaMask: true;\n\n /**\n * Creates a new `MetaMaskInpageProvider`.\n *\n * @param connectionStream - A Node.js duplex stream.\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.shouldSendMetadata - Whether the provider should\n * send page metadata. Default: `true`.\n */\n constructor(\n connectionStream: Duplex,\n {\n logger = console,\n maxEventListeners = 100,\n shouldSendMetadata,\n }: MetaMaskInpageProviderOptions = {},\n ) {\n super(connectionStream, {\n logger,\n maxEventListeners,\n rpcMiddleware: getDefaultExternalMiddleware(logger),\n });\n\n // We shouldn't perform asynchronous work in the constructor, but at one\n // point we started doing so, and changing this class isn't worth it at\n // the time of writing.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this._initializeStateAsync();\n\n this.#networkVersion = null;\n this.isMetaMask = true;\n\n this._sendSync = this._sendSync.bind(this);\n this.enable = this.enable.bind(this);\n this.send = this.send.bind(this);\n this.sendAsync = this.sendAsync.bind(this);\n this._warnOfDeprecation = this._warnOfDeprecation.bind(this);\n\n this._metamask = this._getExperimentalApi();\n\n // handle JSON-RPC notifications\n this._jsonRpcConnection.events.on('notification', (payload) => {\n const { method } = payload;\n if (EMITTED_NOTIFICATIONS.includes(method)) {\n // deprecated\n // emitted here because that was the original order\n this.emit('data', payload);\n // deprecated\n this.emit('notification', payload.params.result);\n }\n });\n\n // send website metadata\n if (shouldSendMetadata) {\n if (document.readyState === 'complete') {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n sendSiteMetadata(this._rpcEngine, this._log);\n } else {\n const domContentLoadedHandler = () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n sendSiteMetadata(this._rpcEngine, this._log);\n window.removeEventListener(\n 'DOMContentLoaded',\n domContentLoadedHandler,\n );\n };\n window.addEventListener('DOMContentLoaded', domContentLoadedHandler);\n }\n }\n }\n\n //====================\n // Read-only Properties\n //====================\n\n get chainId(): string | null {\n return super.chainId;\n }\n\n get networkVersion(): string | null {\n return this.#networkVersion;\n }\n\n get selectedAddress(): string | null {\n return super.selectedAddress;\n }\n\n //====================\n // Public Methods\n //====================\n\n /**\n * Submits an RPC request per the given JSON-RPC request object.\n *\n * @param payload - The RPC request object.\n * @param callback - The callback function.\n */\n sendAsync(\n payload: JsonRpcRequest,\n callback: (error: Error | null, result?: JsonRpcResponse) => void,\n ): void {\n this._rpcRequest(payload, callback);\n }\n\n /**\n * We override the following event methods so that we can warn consumers\n * about deprecated events:\n * `addListener`, `on`, `once`, `prependListener`, `prependOnceListener`.\n */\n\n addListener(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.addListener(eventName, listener);\n }\n\n on(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.on(eventName, listener);\n }\n\n once(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.once(eventName, listener);\n }\n\n prependListener(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.prependListener(eventName, listener);\n }\n\n prependOnceListener(\n eventName: string,\n listener: (...args: unknown[]) => void,\n ) {\n this._warnOfDeprecation(eventName);\n return super.prependOnceListener(eventName, listener);\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * When the provider becomes disconnected, updates internal state and emits\n * required events. Idempotent with respect to the isRecoverable parameter.\n *\n * Error codes per the CloseEvent status codes as required by EIP-1193:\n * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes.\n *\n * @param isRecoverable - Whether the disconnection is recoverable.\n * @param errorMessage - A custom error message.\n * @fires BaseProvider#disconnect - If the disconnection is not recoverable.\n */\n protected _handleDisconnect(isRecoverable: boolean, errorMessage?: string) {\n super._handleDisconnect(isRecoverable, errorMessage);\n if (this.#networkVersion && !isRecoverable) {\n this.#networkVersion = null;\n }\n }\n\n /**\n * Warns of deprecation for the given event, if applicable.\n *\n * @param eventName - The name of the event.\n */\n protected _warnOfDeprecation(eventName: string): void {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare\n if (this._sentWarnings?.events[eventName as WarningEventName] === false) {\n this._log.warn(messages.warnings.events[eventName as WarningEventName]);\n this._sentWarnings.events[eventName as WarningEventName] = true;\n }\n }\n\n //====================\n // Deprecated Methods\n //====================\n\n /**\n * Equivalent to: `ethereum.request('eth_requestAccounts')`.\n *\n * @deprecated Use request({ method: 'eth_requestAccounts' }) instead.\n * @returns A promise that resolves to an array of addresses.\n */\n async enable(): Promise {\n if (!this._sentWarnings.enable) {\n this._log.warn(messages.warnings.enableDeprecation);\n this._sentWarnings.enable = true;\n }\n\n return new Promise((resolve, reject) => {\n try {\n this._rpcRequest(\n { method: 'eth_requestAccounts', params: [] },\n getRpcPromiseCallback(resolve, reject),\n );\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Submits an RPC request for the given method, with the given params.\n *\n * @deprecated Use \"request\" instead.\n * @param method - The method to request.\n * @param params - Any params for the method.\n * @returns A Promise that resolves with the JSON-RPC response object for the\n * request.\n */\n send(\n method: string,\n params?: Type[],\n ): Promise>;\n\n /**\n * Submits an RPC request per the given JSON-RPC request object.\n *\n * @deprecated Use \"request\" instead.\n * @param payload - A JSON-RPC request object.\n * @param callback - An error-first callback that will receive the JSON-RPC\n * response object.\n */\n send(\n payload: JsonRpcRequest,\n callback: (error: Error | null, result?: JsonRpcResponse) => void,\n ): void;\n\n /**\n * Accepts a JSON-RPC request object, and synchronously returns the cached result\n * for the given method. Only supports 4 specific RPC methods.\n *\n * @deprecated Use \"request\" instead.\n * @param payload - A JSON-RPC request object.\n * @returns A JSON-RPC response object.\n */\n send(\n payload: SendSyncJsonRpcRequest,\n ): JsonRpcResponse;\n\n // eslint-disable-next-line @typescript-eslint/promise-function-async\n send(methodOrPayload: unknown, callbackOrArgs?: unknown): unknown {\n if (!this._sentWarnings.send) {\n this._log.warn(messages.warnings.sendDeprecation);\n this._sentWarnings.send = true;\n }\n\n if (\n typeof methodOrPayload === 'string' &&\n (!callbackOrArgs || Array.isArray(callbackOrArgs))\n ) {\n return new Promise((resolve, reject) => {\n try {\n this._rpcRequest(\n { method: methodOrPayload, params: callbackOrArgs },\n getRpcPromiseCallback(resolve, reject, false),\n );\n } catch (error) {\n reject(error);\n }\n });\n } else if (\n methodOrPayload &&\n typeof methodOrPayload === 'object' &&\n typeof callbackOrArgs === 'function'\n ) {\n return this._rpcRequest(\n methodOrPayload as JsonRpcRequest,\n callbackOrArgs as (...args: unknown[]) => void,\n );\n }\n return this._sendSync(methodOrPayload as SendSyncJsonRpcRequest);\n }\n\n /**\n * Internal backwards compatibility method, used in send.\n *\n * @param payload - A JSON-RPC request object.\n * @returns A JSON-RPC response object.\n * @deprecated\n */\n protected _sendSync(payload: SendSyncJsonRpcRequest) {\n let result;\n switch (payload.method) {\n case 'eth_accounts':\n result = this.selectedAddress ? [this.selectedAddress] : [];\n break;\n\n case 'eth_coinbase':\n result = this.selectedAddress ?? null;\n break;\n\n case 'eth_uninstallFilter':\n this._rpcRequest(payload, NOOP);\n result = true;\n break;\n\n case 'net_version':\n result = this.#networkVersion ?? null;\n break;\n\n default:\n throw new Error(messages.errors.unsupportedSync(payload.method));\n }\n\n return {\n id: payload.id,\n jsonrpc: payload.jsonrpc,\n result,\n };\n }\n\n /**\n * Constructor helper.\n *\n * Gets the experimental _metamask API as Proxy, so that we can warn consumers\n * about its experimental nature.\n *\n * @returns The experimental _metamask API.\n */\n protected _getExperimentalApi() {\n return new Proxy(\n {\n /**\n * Determines if MetaMask is unlocked by the user.\n *\n * @returns Promise resolving to true if MetaMask is currently unlocked.\n */\n isUnlocked: async () => {\n if (!this._state.initialized) {\n await new Promise((resolve) => {\n this.on('_initialized', () => resolve());\n });\n }\n return this._state.isUnlocked;\n },\n\n /**\n * Make a batch RPC request.\n *\n * @param requests - The RPC requests to make.\n */\n requestBatch: async (requests: UnvalidatedJsonRpcRequest[]) => {\n if (!Array.isArray(requests)) {\n throw rpcErrors.invalidRequest({\n message:\n 'Batch requests must be made with an array of request objects.',\n data: requests,\n });\n }\n\n return new Promise((resolve, reject) => {\n this._rpcRequest(requests, getRpcPromiseCallback(resolve, reject));\n });\n },\n },\n {\n get: (obj, prop, ...args) => {\n if (!this._sentWarnings.experimentalMethods) {\n this._log.warn(messages.warnings.experimentalMethods);\n this._sentWarnings.experimentalMethods = true;\n }\n return Reflect.get(obj, prop, ...args);\n },\n },\n );\n }\n\n /**\n * Upon receipt of a new chainId, networkVersion, and isConnected value\n * emits corresponding events and sets relevant public state. We interpret\n * a `networkVersion` with the value of `loading` to be null. The `isConnected`\n * value determines if a `connect` or recoverable `disconnect` has occurred.\n * Child classes that use the `networkVersion` for other purposes must implement\n * additional handling therefore.\n *\n * @fires MetamaskInpageProvider#networkChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n * @param networkInfo.networkVersion - The latest network ID.\n * @param networkInfo.isConnected - Whether the network is available.\n */\n protected _handleChainChanged({\n chainId,\n networkVersion,\n isConnected,\n }: {\n chainId?: string;\n networkVersion?: string;\n isConnected?: boolean;\n } = {}) {\n super._handleChainChanged({ chainId, networkVersion, isConnected });\n\n // The wallet will send a value of `loading` for `networkVersion` when it intends\n // to communicate that this value cannot be resolved and should be intepreted as null.\n // The wallet cannot directly send a null value for `networkVersion` because this\n // would be a breaking change for existing dapps that use their own embedded MetaMask provider\n // that expect this value to always be a integer string or the value 'loading'.\n\n const targetNetworkVersion =\n networkVersion === 'loading' ? null : networkVersion;\n\n if (targetNetworkVersion !== this.#networkVersion) {\n this.#networkVersion = targetNetworkVersion as string;\n if (this._state.initialized) {\n this.emit('networkChanged', this.#networkVersion);\n }\n }\n }\n}\n"]} -\ No newline at end of file -diff --git a/dist/MetaMaskInpageProvider.d.cts b/dist/MetaMaskInpageProvider.d.cts -index 43e0aa01ff7b83932f7fd795b77a3f2b8d2d446f..97e931abef5ad17d3915b7fd8de633728633f1b3 100644 ---- a/dist/MetaMaskInpageProvider.d.cts -+++ b/dist/MetaMaskInpageProvider.d.cts -@@ -158,18 +158,23 @@ export declare class MetaMaskInpageProvider extends AbstractStreamProvider { - requestBatch: (requests: UnvalidatedJsonRpcRequest[]) => Promise; - }; - /** -- * Upon receipt of a new chainId and networkVersion, emits corresponding -- * events and sets relevant public state. Does nothing if neither the chainId -- * nor the networkVersion are different from existing values. -+ * Upon receipt of a new chainId, networkVersion, and isConnected value -+ * emits corresponding events and sets relevant public state. We interpret -+ * a `networkVersion` with the value of `loading` to be null. The `isConnected` -+ * value determines if a `connect` or recoverable `disconnect` has occurred. -+ * Child classes that use the `networkVersion` for other purposes must implement -+ * additional handling therefore. - * - * @fires MetamaskInpageProvider#networkChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. - * @param networkInfo.networkVersion - The latest network ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- protected _handleChainChanged({ chainId, networkVersion, }?: { -+ protected _handleChainChanged({ chainId, networkVersion, isConnected, }?: { - chainId?: string; - networkVersion?: string; -+ isConnected?: boolean; - }): void; - } - export {}; -diff --git a/dist/MetaMaskInpageProvider.d.cts.map b/dist/MetaMaskInpageProvider.d.cts.map -index b579441774c22c62fccee858f358163fbc33bc1d..f4ab45d111c3dbee94665a83dc03d1b7aed573ef 100644 ---- a/dist/MetaMaskInpageProvider.d.cts.map -+++ b/dist/MetaMaskInpageProvider.d.cts.map -@@ -1 +1 @@ --{"version":3,"file":"MetaMaskInpageProvider.d.cts","sourceRoot":"","sources":["../src/MetaMaskInpageProvider.ts"],"names":[],"mappings":"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"} -\ No newline at end of file -+{"version":3,"file":"MetaMaskInpageProvider.d.cts","sourceRoot":"","sources":["../src/MetaMaskInpageProvider.ts"],"names":[],"mappings":"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"} -\ No newline at end of file -diff --git a/dist/MetaMaskInpageProvider.d.mts b/dist/MetaMaskInpageProvider.d.mts -index c768a628fe2e0d8a393c3f0dee55ae9dbb5961fa..96af9efbe01fbc0e05c6c53e1797a7e62e1f55aa 100644 ---- a/dist/MetaMaskInpageProvider.d.mts -+++ b/dist/MetaMaskInpageProvider.d.mts -@@ -158,18 +158,23 @@ export declare class MetaMaskInpageProvider extends AbstractStreamProvider { - requestBatch: (requests: UnvalidatedJsonRpcRequest[]) => Promise; - }; - /** -- * Upon receipt of a new chainId and networkVersion, emits corresponding -- * events and sets relevant public state. Does nothing if neither the chainId -- * nor the networkVersion are different from existing values. -+ * Upon receipt of a new chainId, networkVersion, and isConnected value -+ * emits corresponding events and sets relevant public state. We interpret -+ * a `networkVersion` with the value of `loading` to be null. The `isConnected` -+ * value determines if a `connect` or recoverable `disconnect` has occurred. -+ * Child classes that use the `networkVersion` for other purposes must implement -+ * additional handling therefore. - * - * @fires MetamaskInpageProvider#networkChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. - * @param networkInfo.networkVersion - The latest network ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- protected _handleChainChanged({ chainId, networkVersion, }?: { -+ protected _handleChainChanged({ chainId, networkVersion, isConnected, }?: { - chainId?: string; - networkVersion?: string; -+ isConnected?: boolean; - }): void; - } - export {}; -diff --git a/dist/MetaMaskInpageProvider.d.mts.map b/dist/MetaMaskInpageProvider.d.mts.map -index 72cc5f19d4b9cbf6198eefbb8cdd6bd3fbc218f1..7c7027194af4ddcb4d4c844a56a7066719c1136b 100644 ---- a/dist/MetaMaskInpageProvider.d.mts.map -+++ b/dist/MetaMaskInpageProvider.d.mts.map -@@ -1 +1 @@ --{"version":3,"file":"MetaMaskInpageProvider.d.mts","sourceRoot":"","sources":["../src/MetaMaskInpageProvider.ts"],"names":[],"mappings":"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"} -\ No newline at end of file -+{"version":3,"file":"MetaMaskInpageProvider.d.mts","sourceRoot":"","sources":["../src/MetaMaskInpageProvider.ts"],"names":[],"mappings":"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"} -\ No newline at end of file -diff --git a/dist/MetaMaskInpageProvider.mjs b/dist/MetaMaskInpageProvider.mjs -index 9f6f31d8eb7ab361d0372866b152dd57d62fc0d5..451652ff976f34a2cdea154c963c1a120edd2af0 100644 ---- a/dist/MetaMaskInpageProvider.mjs -+++ b/dist/MetaMaskInpageProvider.mjs -@@ -301,21 +301,29 @@ export class MetaMaskInpageProvider extends AbstractStreamProvider { - }); - } - /** -- * Upon receipt of a new chainId and networkVersion, emits corresponding -- * events and sets relevant public state. Does nothing if neither the chainId -- * nor the networkVersion are different from existing values. -+ * Upon receipt of a new chainId, networkVersion, and isConnected value -+ * emits corresponding events and sets relevant public state. We interpret -+ * a `networkVersion` with the value of `loading` to be null. The `isConnected` -+ * value determines if a `connect` or recoverable `disconnect` has occurred. -+ * Child classes that use the `networkVersion` for other purposes must implement -+ * additional handling therefore. - * - * @fires MetamaskInpageProvider#networkChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. - * @param networkInfo.networkVersion - The latest network ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- _handleChainChanged({ chainId, networkVersion, } = {}) { -- // This will validate the params and disconnect the provider if the -- // networkVersion is 'loading'. -- super._handleChainChanged({ chainId, networkVersion }); -- if (this._state.isConnected && networkVersion !== __classPrivateFieldGet(this, _MetaMaskInpageProvider_networkVersion, "f")) { -- __classPrivateFieldSet(this, _MetaMaskInpageProvider_networkVersion, networkVersion, "f"); -+ _handleChainChanged({ chainId, networkVersion, isConnected, } = {}) { -+ super._handleChainChanged({ chainId, networkVersion, isConnected }); -+ // The wallet will send a value of `loading` for `networkVersion` when it intends -+ // to communicate that this value cannot be resolved and should be intepreted as null. -+ // The wallet cannot directly send a null value for `networkVersion` because this -+ // would be a breaking change for existing dapps that use their own embedded MetaMask provider -+ // that expect this value to always be a integer string or the value 'loading'. -+ const targetNetworkVersion = networkVersion === 'loading' ? null : networkVersion; -+ if (targetNetworkVersion !== __classPrivateFieldGet(this, _MetaMaskInpageProvider_networkVersion, "f")) { -+ __classPrivateFieldSet(this, _MetaMaskInpageProvider_networkVersion, targetNetworkVersion, "f"); - if (this._state.initialized) { - this.emit('networkChanged', __classPrivateFieldGet(this, _MetaMaskInpageProvider_networkVersion, "f")); - } -diff --git a/dist/MetaMaskInpageProvider.mjs.map b/dist/MetaMaskInpageProvider.mjs.map -index f57395722fe8e423cf19229b8940fcf0ed843415..97448e1af39416b71bb50582f6ef7cb6a39b3cad 100644 ---- a/dist/MetaMaskInpageProvider.mjs.map -+++ b/dist/MetaMaskInpageProvider.mjs.map -@@ -1 +1 @@ --{"version":3,"file":"MetaMaskInpageProvider.mjs","sourceRoot":"","sources":["../src/MetaMaskInpageProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import { rpcErrors } from '@metamask/rpc-errors';\nimport type { Json, JsonRpcRequest, JsonRpcResponse } from '@metamask/utils';\nimport type { Duplex } from 'readable-stream';\n\nimport type { UnvalidatedJsonRpcRequest } from './BaseProvider';\nimport messages from './messages';\nimport { sendSiteMetadata } from './siteMetadata';\nimport type { StreamProviderOptions } from './StreamProvider';\nimport { AbstractStreamProvider } from './StreamProvider';\nimport {\n EMITTED_NOTIFICATIONS,\n getDefaultExternalMiddleware,\n getRpcPromiseCallback,\n NOOP,\n} from './utils';\n\nexport type SendSyncJsonRpcRequest = {\n method:\n | 'eth_accounts'\n | 'eth_coinbase'\n | 'eth_uninstallFilter'\n | 'net_version';\n} & JsonRpcRequest;\n\ntype WarningEventName = keyof SentWarningsState['events'];\n\nexport type MetaMaskInpageProviderOptions = {\n /**\n * Whether the provider should send page metadata.\n */\n shouldSendMetadata?: boolean;\n} & Partial>;\n\ntype SentWarningsState = {\n // methods\n enable: boolean;\n experimentalMethods: boolean;\n send: boolean;\n // events\n events: {\n close: boolean;\n data: boolean;\n networkChanged: boolean;\n notification: boolean;\n };\n};\n\n/**\n * The name of the stream consumed by {@link MetaMaskInpageProvider}.\n */\nexport const MetaMaskInpageProviderStreamName = 'metamask-provider';\n\nexport class MetaMaskInpageProvider extends AbstractStreamProvider {\n protected _sentWarnings: SentWarningsState = {\n // methods\n enable: false,\n experimentalMethods: false,\n send: false,\n // events\n events: {\n close: false,\n data: false,\n networkChanged: false,\n notification: false,\n },\n };\n\n /**\n * Experimental methods can be found here.\n */\n public readonly _metamask: ReturnType<\n MetaMaskInpageProvider['_getExperimentalApi']\n >;\n\n #networkVersion: string | null;\n\n /**\n * Indicating that this provider is a MetaMask provider.\n */\n public readonly isMetaMask: true;\n\n /**\n * Creates a new `MetaMaskInpageProvider`.\n *\n * @param connectionStream - A Node.js duplex stream.\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.shouldSendMetadata - Whether the provider should\n * send page metadata. Default: `true`.\n */\n constructor(\n connectionStream: Duplex,\n {\n logger = console,\n maxEventListeners = 100,\n shouldSendMetadata,\n }: MetaMaskInpageProviderOptions = {},\n ) {\n super(connectionStream, {\n logger,\n maxEventListeners,\n rpcMiddleware: getDefaultExternalMiddleware(logger),\n });\n\n // We shouldn't perform asynchronous work in the constructor, but at one\n // point we started doing so, and changing this class isn't worth it at\n // the time of writing.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this._initializeStateAsync();\n\n this.#networkVersion = null;\n this.isMetaMask = true;\n\n this._sendSync = this._sendSync.bind(this);\n this.enable = this.enable.bind(this);\n this.send = this.send.bind(this);\n this.sendAsync = this.sendAsync.bind(this);\n this._warnOfDeprecation = this._warnOfDeprecation.bind(this);\n\n this._metamask = this._getExperimentalApi();\n\n // handle JSON-RPC notifications\n this._jsonRpcConnection.events.on('notification', (payload) => {\n const { method } = payload;\n if (EMITTED_NOTIFICATIONS.includes(method)) {\n // deprecated\n // emitted here because that was the original order\n this.emit('data', payload);\n // deprecated\n this.emit('notification', payload.params.result);\n }\n });\n\n // send website metadata\n if (shouldSendMetadata) {\n if (document.readyState === 'complete') {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n sendSiteMetadata(this._rpcEngine, this._log);\n } else {\n const domContentLoadedHandler = () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n sendSiteMetadata(this._rpcEngine, this._log);\n window.removeEventListener(\n 'DOMContentLoaded',\n domContentLoadedHandler,\n );\n };\n window.addEventListener('DOMContentLoaded', domContentLoadedHandler);\n }\n }\n }\n\n //====================\n // Read-only Properties\n //====================\n\n get chainId(): string | null {\n return super.chainId;\n }\n\n get networkVersion(): string | null {\n return this.#networkVersion;\n }\n\n get selectedAddress(): string | null {\n return super.selectedAddress;\n }\n\n //====================\n // Public Methods\n //====================\n\n /**\n * Submits an RPC request per the given JSON-RPC request object.\n *\n * @param payload - The RPC request object.\n * @param callback - The callback function.\n */\n sendAsync(\n payload: JsonRpcRequest,\n callback: (error: Error | null, result?: JsonRpcResponse) => void,\n ): void {\n this._rpcRequest(payload, callback);\n }\n\n /**\n * We override the following event methods so that we can warn consumers\n * about deprecated events:\n * `addListener`, `on`, `once`, `prependListener`, `prependOnceListener`.\n */\n\n addListener(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.addListener(eventName, listener);\n }\n\n on(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.on(eventName, listener);\n }\n\n once(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.once(eventName, listener);\n }\n\n prependListener(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.prependListener(eventName, listener);\n }\n\n prependOnceListener(\n eventName: string,\n listener: (...args: unknown[]) => void,\n ) {\n this._warnOfDeprecation(eventName);\n return super.prependOnceListener(eventName, listener);\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * When the provider becomes disconnected, updates internal state and emits\n * required events. Idempotent with respect to the isRecoverable parameter.\n *\n * Error codes per the CloseEvent status codes as required by EIP-1193:\n * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes.\n *\n * @param isRecoverable - Whether the disconnection is recoverable.\n * @param errorMessage - A custom error message.\n * @fires BaseProvider#disconnect - If the disconnection is not recoverable.\n */\n protected _handleDisconnect(isRecoverable: boolean, errorMessage?: string) {\n super._handleDisconnect(isRecoverable, errorMessage);\n if (this.#networkVersion && !isRecoverable) {\n this.#networkVersion = null;\n }\n }\n\n /**\n * Warns of deprecation for the given event, if applicable.\n *\n * @param eventName - The name of the event.\n */\n protected _warnOfDeprecation(eventName: string): void {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare\n if (this._sentWarnings?.events[eventName as WarningEventName] === false) {\n this._log.warn(messages.warnings.events[eventName as WarningEventName]);\n this._sentWarnings.events[eventName as WarningEventName] = true;\n }\n }\n\n //====================\n // Deprecated Methods\n //====================\n\n /**\n * Equivalent to: `ethereum.request('eth_requestAccounts')`.\n *\n * @deprecated Use request({ method: 'eth_requestAccounts' }) instead.\n * @returns A promise that resolves to an array of addresses.\n */\n async enable(): Promise {\n if (!this._sentWarnings.enable) {\n this._log.warn(messages.warnings.enableDeprecation);\n this._sentWarnings.enable = true;\n }\n\n return new Promise((resolve, reject) => {\n try {\n this._rpcRequest(\n { method: 'eth_requestAccounts', params: [] },\n getRpcPromiseCallback(resolve, reject),\n );\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Submits an RPC request for the given method, with the given params.\n *\n * @deprecated Use \"request\" instead.\n * @param method - The method to request.\n * @param params - Any params for the method.\n * @returns A Promise that resolves with the JSON-RPC response object for the\n * request.\n */\n send(\n method: string,\n params?: Type[],\n ): Promise>;\n\n /**\n * Submits an RPC request per the given JSON-RPC request object.\n *\n * @deprecated Use \"request\" instead.\n * @param payload - A JSON-RPC request object.\n * @param callback - An error-first callback that will receive the JSON-RPC\n * response object.\n */\n send(\n payload: JsonRpcRequest,\n callback: (error: Error | null, result?: JsonRpcResponse) => void,\n ): void;\n\n /**\n * Accepts a JSON-RPC request object, and synchronously returns the cached result\n * for the given method. Only supports 4 specific RPC methods.\n *\n * @deprecated Use \"request\" instead.\n * @param payload - A JSON-RPC request object.\n * @returns A JSON-RPC response object.\n */\n send(\n payload: SendSyncJsonRpcRequest,\n ): JsonRpcResponse;\n\n // eslint-disable-next-line @typescript-eslint/promise-function-async\n send(methodOrPayload: unknown, callbackOrArgs?: unknown): unknown {\n if (!this._sentWarnings.send) {\n this._log.warn(messages.warnings.sendDeprecation);\n this._sentWarnings.send = true;\n }\n\n if (\n typeof methodOrPayload === 'string' &&\n (!callbackOrArgs || Array.isArray(callbackOrArgs))\n ) {\n return new Promise((resolve, reject) => {\n try {\n this._rpcRequest(\n { method: methodOrPayload, params: callbackOrArgs },\n getRpcPromiseCallback(resolve, reject, false),\n );\n } catch (error) {\n reject(error);\n }\n });\n } else if (\n methodOrPayload &&\n typeof methodOrPayload === 'object' &&\n typeof callbackOrArgs === 'function'\n ) {\n return this._rpcRequest(\n methodOrPayload as JsonRpcRequest,\n callbackOrArgs as (...args: unknown[]) => void,\n );\n }\n return this._sendSync(methodOrPayload as SendSyncJsonRpcRequest);\n }\n\n /**\n * Internal backwards compatibility method, used in send.\n *\n * @param payload - A JSON-RPC request object.\n * @returns A JSON-RPC response object.\n * @deprecated\n */\n protected _sendSync(payload: SendSyncJsonRpcRequest) {\n let result;\n switch (payload.method) {\n case 'eth_accounts':\n result = this.selectedAddress ? [this.selectedAddress] : [];\n break;\n\n case 'eth_coinbase':\n result = this.selectedAddress ?? null;\n break;\n\n case 'eth_uninstallFilter':\n this._rpcRequest(payload, NOOP);\n result = true;\n break;\n\n case 'net_version':\n result = this.#networkVersion ?? null;\n break;\n\n default:\n throw new Error(messages.errors.unsupportedSync(payload.method));\n }\n\n return {\n id: payload.id,\n jsonrpc: payload.jsonrpc,\n result,\n };\n }\n\n /**\n * Constructor helper.\n *\n * Gets the experimental _metamask API as Proxy, so that we can warn consumers\n * about its experimental nature.\n *\n * @returns The experimental _metamask API.\n */\n protected _getExperimentalApi() {\n return new Proxy(\n {\n /**\n * Determines if MetaMask is unlocked by the user.\n *\n * @returns Promise resolving to true if MetaMask is currently unlocked.\n */\n isUnlocked: async () => {\n if (!this._state.initialized) {\n await new Promise((resolve) => {\n this.on('_initialized', () => resolve());\n });\n }\n return this._state.isUnlocked;\n },\n\n /**\n * Make a batch RPC request.\n *\n * @param requests - The RPC requests to make.\n */\n requestBatch: async (requests: UnvalidatedJsonRpcRequest[]) => {\n if (!Array.isArray(requests)) {\n throw rpcErrors.invalidRequest({\n message:\n 'Batch requests must be made with an array of request objects.',\n data: requests,\n });\n }\n\n return new Promise((resolve, reject) => {\n this._rpcRequest(requests, getRpcPromiseCallback(resolve, reject));\n });\n },\n },\n {\n get: (obj, prop, ...args) => {\n if (!this._sentWarnings.experimentalMethods) {\n this._log.warn(messages.warnings.experimentalMethods);\n this._sentWarnings.experimentalMethods = true;\n }\n return Reflect.get(obj, prop, ...args);\n },\n },\n );\n }\n\n /**\n * Upon receipt of a new chainId and networkVersion, emits corresponding\n * events and sets relevant public state. Does nothing if neither the chainId\n * nor the networkVersion are different from existing values.\n *\n * @fires MetamaskInpageProvider#networkChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n * @param networkInfo.networkVersion - The latest network ID.\n */\n protected _handleChainChanged({\n chainId,\n networkVersion,\n }: { chainId?: string; networkVersion?: string } = {}) {\n // This will validate the params and disconnect the provider if the\n // networkVersion is 'loading'.\n super._handleChainChanged({ chainId, networkVersion });\n\n if (this._state.isConnected && networkVersion !== this.#networkVersion) {\n this.#networkVersion = networkVersion as string;\n if (this._state.initialized) {\n this.emit('networkChanged', this.#networkVersion);\n }\n }\n }\n}\n"]} -\ No newline at end of file -+{"version":3,"file":"MetaMaskInpageProvider.mjs","sourceRoot":"","sources":["../src/MetaMaskInpageProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import { rpcErrors } from '@metamask/rpc-errors';\nimport type { Json, JsonRpcRequest, JsonRpcResponse } from '@metamask/utils';\nimport type { Duplex } from 'readable-stream';\n\nimport type { UnvalidatedJsonRpcRequest } from './BaseProvider';\nimport messages from './messages';\nimport { sendSiteMetadata } from './siteMetadata';\nimport type { StreamProviderOptions } from './StreamProvider';\nimport { AbstractStreamProvider } from './StreamProvider';\nimport {\n EMITTED_NOTIFICATIONS,\n getDefaultExternalMiddleware,\n getRpcPromiseCallback,\n NOOP,\n} from './utils';\n\nexport type SendSyncJsonRpcRequest = {\n method:\n | 'eth_accounts'\n | 'eth_coinbase'\n | 'eth_uninstallFilter'\n | 'net_version';\n} & JsonRpcRequest;\n\ntype WarningEventName = keyof SentWarningsState['events'];\n\nexport type MetaMaskInpageProviderOptions = {\n /**\n * Whether the provider should send page metadata.\n */\n shouldSendMetadata?: boolean;\n} & Partial>;\n\ntype SentWarningsState = {\n // methods\n enable: boolean;\n experimentalMethods: boolean;\n send: boolean;\n // events\n events: {\n close: boolean;\n data: boolean;\n networkChanged: boolean;\n notification: boolean;\n };\n};\n\n/**\n * The name of the stream consumed by {@link MetaMaskInpageProvider}.\n */\nexport const MetaMaskInpageProviderStreamName = 'metamask-provider';\n\nexport class MetaMaskInpageProvider extends AbstractStreamProvider {\n protected _sentWarnings: SentWarningsState = {\n // methods\n enable: false,\n experimentalMethods: false,\n send: false,\n // events\n events: {\n close: false,\n data: false,\n networkChanged: false,\n notification: false,\n },\n };\n\n /**\n * Experimental methods can be found here.\n */\n public readonly _metamask: ReturnType<\n MetaMaskInpageProvider['_getExperimentalApi']\n >;\n\n #networkVersion: string | null;\n\n /**\n * Indicating that this provider is a MetaMask provider.\n */\n public readonly isMetaMask: true;\n\n /**\n * Creates a new `MetaMaskInpageProvider`.\n *\n * @param connectionStream - A Node.js duplex stream.\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.shouldSendMetadata - Whether the provider should\n * send page metadata. Default: `true`.\n */\n constructor(\n connectionStream: Duplex,\n {\n logger = console,\n maxEventListeners = 100,\n shouldSendMetadata,\n }: MetaMaskInpageProviderOptions = {},\n ) {\n super(connectionStream, {\n logger,\n maxEventListeners,\n rpcMiddleware: getDefaultExternalMiddleware(logger),\n });\n\n // We shouldn't perform asynchronous work in the constructor, but at one\n // point we started doing so, and changing this class isn't worth it at\n // the time of writing.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this._initializeStateAsync();\n\n this.#networkVersion = null;\n this.isMetaMask = true;\n\n this._sendSync = this._sendSync.bind(this);\n this.enable = this.enable.bind(this);\n this.send = this.send.bind(this);\n this.sendAsync = this.sendAsync.bind(this);\n this._warnOfDeprecation = this._warnOfDeprecation.bind(this);\n\n this._metamask = this._getExperimentalApi();\n\n // handle JSON-RPC notifications\n this._jsonRpcConnection.events.on('notification', (payload) => {\n const { method } = payload;\n if (EMITTED_NOTIFICATIONS.includes(method)) {\n // deprecated\n // emitted here because that was the original order\n this.emit('data', payload);\n // deprecated\n this.emit('notification', payload.params.result);\n }\n });\n\n // send website metadata\n if (shouldSendMetadata) {\n if (document.readyState === 'complete') {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n sendSiteMetadata(this._rpcEngine, this._log);\n } else {\n const domContentLoadedHandler = () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n sendSiteMetadata(this._rpcEngine, this._log);\n window.removeEventListener(\n 'DOMContentLoaded',\n domContentLoadedHandler,\n );\n };\n window.addEventListener('DOMContentLoaded', domContentLoadedHandler);\n }\n }\n }\n\n //====================\n // Read-only Properties\n //====================\n\n get chainId(): string | null {\n return super.chainId;\n }\n\n get networkVersion(): string | null {\n return this.#networkVersion;\n }\n\n get selectedAddress(): string | null {\n return super.selectedAddress;\n }\n\n //====================\n // Public Methods\n //====================\n\n /**\n * Submits an RPC request per the given JSON-RPC request object.\n *\n * @param payload - The RPC request object.\n * @param callback - The callback function.\n */\n sendAsync(\n payload: JsonRpcRequest,\n callback: (error: Error | null, result?: JsonRpcResponse) => void,\n ): void {\n this._rpcRequest(payload, callback);\n }\n\n /**\n * We override the following event methods so that we can warn consumers\n * about deprecated events:\n * `addListener`, `on`, `once`, `prependListener`, `prependOnceListener`.\n */\n\n addListener(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.addListener(eventName, listener);\n }\n\n on(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.on(eventName, listener);\n }\n\n once(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.once(eventName, listener);\n }\n\n prependListener(eventName: string, listener: (...args: unknown[]) => void) {\n this._warnOfDeprecation(eventName);\n return super.prependListener(eventName, listener);\n }\n\n prependOnceListener(\n eventName: string,\n listener: (...args: unknown[]) => void,\n ) {\n this._warnOfDeprecation(eventName);\n return super.prependOnceListener(eventName, listener);\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * When the provider becomes disconnected, updates internal state and emits\n * required events. Idempotent with respect to the isRecoverable parameter.\n *\n * Error codes per the CloseEvent status codes as required by EIP-1193:\n * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes.\n *\n * @param isRecoverable - Whether the disconnection is recoverable.\n * @param errorMessage - A custom error message.\n * @fires BaseProvider#disconnect - If the disconnection is not recoverable.\n */\n protected _handleDisconnect(isRecoverable: boolean, errorMessage?: string) {\n super._handleDisconnect(isRecoverable, errorMessage);\n if (this.#networkVersion && !isRecoverable) {\n this.#networkVersion = null;\n }\n }\n\n /**\n * Warns of deprecation for the given event, if applicable.\n *\n * @param eventName - The name of the event.\n */\n protected _warnOfDeprecation(eventName: string): void {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare\n if (this._sentWarnings?.events[eventName as WarningEventName] === false) {\n this._log.warn(messages.warnings.events[eventName as WarningEventName]);\n this._sentWarnings.events[eventName as WarningEventName] = true;\n }\n }\n\n //====================\n // Deprecated Methods\n //====================\n\n /**\n * Equivalent to: `ethereum.request('eth_requestAccounts')`.\n *\n * @deprecated Use request({ method: 'eth_requestAccounts' }) instead.\n * @returns A promise that resolves to an array of addresses.\n */\n async enable(): Promise {\n if (!this._sentWarnings.enable) {\n this._log.warn(messages.warnings.enableDeprecation);\n this._sentWarnings.enable = true;\n }\n\n return new Promise((resolve, reject) => {\n try {\n this._rpcRequest(\n { method: 'eth_requestAccounts', params: [] },\n getRpcPromiseCallback(resolve, reject),\n );\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Submits an RPC request for the given method, with the given params.\n *\n * @deprecated Use \"request\" instead.\n * @param method - The method to request.\n * @param params - Any params for the method.\n * @returns A Promise that resolves with the JSON-RPC response object for the\n * request.\n */\n send(\n method: string,\n params?: Type[],\n ): Promise>;\n\n /**\n * Submits an RPC request per the given JSON-RPC request object.\n *\n * @deprecated Use \"request\" instead.\n * @param payload - A JSON-RPC request object.\n * @param callback - An error-first callback that will receive the JSON-RPC\n * response object.\n */\n send(\n payload: JsonRpcRequest,\n callback: (error: Error | null, result?: JsonRpcResponse) => void,\n ): void;\n\n /**\n * Accepts a JSON-RPC request object, and synchronously returns the cached result\n * for the given method. Only supports 4 specific RPC methods.\n *\n * @deprecated Use \"request\" instead.\n * @param payload - A JSON-RPC request object.\n * @returns A JSON-RPC response object.\n */\n send(\n payload: SendSyncJsonRpcRequest,\n ): JsonRpcResponse;\n\n // eslint-disable-next-line @typescript-eslint/promise-function-async\n send(methodOrPayload: unknown, callbackOrArgs?: unknown): unknown {\n if (!this._sentWarnings.send) {\n this._log.warn(messages.warnings.sendDeprecation);\n this._sentWarnings.send = true;\n }\n\n if (\n typeof methodOrPayload === 'string' &&\n (!callbackOrArgs || Array.isArray(callbackOrArgs))\n ) {\n return new Promise((resolve, reject) => {\n try {\n this._rpcRequest(\n { method: methodOrPayload, params: callbackOrArgs },\n getRpcPromiseCallback(resolve, reject, false),\n );\n } catch (error) {\n reject(error);\n }\n });\n } else if (\n methodOrPayload &&\n typeof methodOrPayload === 'object' &&\n typeof callbackOrArgs === 'function'\n ) {\n return this._rpcRequest(\n methodOrPayload as JsonRpcRequest,\n callbackOrArgs as (...args: unknown[]) => void,\n );\n }\n return this._sendSync(methodOrPayload as SendSyncJsonRpcRequest);\n }\n\n /**\n * Internal backwards compatibility method, used in send.\n *\n * @param payload - A JSON-RPC request object.\n * @returns A JSON-RPC response object.\n * @deprecated\n */\n protected _sendSync(payload: SendSyncJsonRpcRequest) {\n let result;\n switch (payload.method) {\n case 'eth_accounts':\n result = this.selectedAddress ? [this.selectedAddress] : [];\n break;\n\n case 'eth_coinbase':\n result = this.selectedAddress ?? null;\n break;\n\n case 'eth_uninstallFilter':\n this._rpcRequest(payload, NOOP);\n result = true;\n break;\n\n case 'net_version':\n result = this.#networkVersion ?? null;\n break;\n\n default:\n throw new Error(messages.errors.unsupportedSync(payload.method));\n }\n\n return {\n id: payload.id,\n jsonrpc: payload.jsonrpc,\n result,\n };\n }\n\n /**\n * Constructor helper.\n *\n * Gets the experimental _metamask API as Proxy, so that we can warn consumers\n * about its experimental nature.\n *\n * @returns The experimental _metamask API.\n */\n protected _getExperimentalApi() {\n return new Proxy(\n {\n /**\n * Determines if MetaMask is unlocked by the user.\n *\n * @returns Promise resolving to true if MetaMask is currently unlocked.\n */\n isUnlocked: async () => {\n if (!this._state.initialized) {\n await new Promise((resolve) => {\n this.on('_initialized', () => resolve());\n });\n }\n return this._state.isUnlocked;\n },\n\n /**\n * Make a batch RPC request.\n *\n * @param requests - The RPC requests to make.\n */\n requestBatch: async (requests: UnvalidatedJsonRpcRequest[]) => {\n if (!Array.isArray(requests)) {\n throw rpcErrors.invalidRequest({\n message:\n 'Batch requests must be made with an array of request objects.',\n data: requests,\n });\n }\n\n return new Promise((resolve, reject) => {\n this._rpcRequest(requests, getRpcPromiseCallback(resolve, reject));\n });\n },\n },\n {\n get: (obj, prop, ...args) => {\n if (!this._sentWarnings.experimentalMethods) {\n this._log.warn(messages.warnings.experimentalMethods);\n this._sentWarnings.experimentalMethods = true;\n }\n return Reflect.get(obj, prop, ...args);\n },\n },\n );\n }\n\n /**\n * Upon receipt of a new chainId, networkVersion, and isConnected value\n * emits corresponding events and sets relevant public state. We interpret\n * a `networkVersion` with the value of `loading` to be null. The `isConnected`\n * value determines if a `connect` or recoverable `disconnect` has occurred.\n * Child classes that use the `networkVersion` for other purposes must implement\n * additional handling therefore.\n *\n * @fires MetamaskInpageProvider#networkChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n * @param networkInfo.networkVersion - The latest network ID.\n * @param networkInfo.isConnected - Whether the network is available.\n */\n protected _handleChainChanged({\n chainId,\n networkVersion,\n isConnected,\n }: {\n chainId?: string;\n networkVersion?: string;\n isConnected?: boolean;\n } = {}) {\n super._handleChainChanged({ chainId, networkVersion, isConnected });\n\n // The wallet will send a value of `loading` for `networkVersion` when it intends\n // to communicate that this value cannot be resolved and should be intepreted as null.\n // The wallet cannot directly send a null value for `networkVersion` because this\n // would be a breaking change for existing dapps that use their own embedded MetaMask provider\n // that expect this value to always be a integer string or the value 'loading'.\n\n const targetNetworkVersion =\n networkVersion === 'loading' ? null : networkVersion;\n\n if (targetNetworkVersion !== this.#networkVersion) {\n this.#networkVersion = targetNetworkVersion as string;\n if (this._state.initialized) {\n this.emit('networkChanged', this.#networkVersion);\n }\n }\n }\n}\n"]} -\ No newline at end of file -diff --git a/dist/StreamProvider.cjs b/dist/StreamProvider.cjs -index 4c7ffee4be4e69b2103a7f8d3cf942a7b5e8feb2..10247263488f26f5567d0877038255b16ff15b39 100644 ---- a/dist/StreamProvider.cjs -+++ b/dist/StreamProvider.cjs -@@ -111,19 +111,16 @@ class AbstractStreamProvider extends BaseProvider_1.BaseProvider { - } - /** - * Upon receipt of a new chainId and networkVersion, emits corresponding -- * events and sets relevant public state. This class does not have a -- * `networkVersion` property, but we rely on receiving a `networkVersion` -- * with the value of `loading` to detect when the network is changing and -- * a recoverable `disconnect` even has occurred. Child classes that use the -- * `networkVersion` for other purposes must implement additional handling -- * therefore. -+ * events and sets relevant public state. Child classes that use the -+ * `networkVersion` for other purposes must implement additional handling. - * - * @fires BaseProvider#chainChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. - * @param networkInfo.networkVersion - The latest network ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- _handleChainChanged({ chainId, networkVersion, } = {}) { -+ _handleChainChanged({ chainId, networkVersion, isConnected, } = {}) { - if (!(0, utils_1.isValidChainId)(chainId) || !(0, utils_1.isValidNetworkVersion)(networkVersion)) { - this._log.error(messages_1.default.errors.invalidNetworkParams(), { - chainId, -@@ -131,12 +128,10 @@ class AbstractStreamProvider extends BaseProvider_1.BaseProvider { - }); - return; - } -- if (networkVersion === 'loading') { -+ super._handleChainChanged({ chainId, isConnected }); -+ if (!isConnected) { - this._handleDisconnect(true); - } -- else { -- super._handleChainChanged({ chainId }); -- } - } - } - exports.AbstractStreamProvider = AbstractStreamProvider; -diff --git a/dist/StreamProvider.cjs.map b/dist/StreamProvider.cjs.map -index 26cc2ecb6d0e4d3f12dacc03217cf685c45c17aa..ad871b11033d01bf0848db844ffb8f51981ae97f 100644 ---- a/dist/StreamProvider.cjs.map -+++ b/dist/StreamProvider.cjs.map -@@ -1 +1 @@ --{"version":3,"file":"StreamProvider.cjs","sourceRoot":"","sources":["../src/StreamProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createStreamMiddleware } from '@metamask/json-rpc-middleware-stream';\nimport type SafeEventEmitter from '@metamask/safe-event-emitter';\nimport type { Json, JsonRpcParams } from '@metamask/utils';\nimport { duplex as isDuplex } from 'is-stream';\nimport { pipeline } from 'readable-stream';\nimport type { Duplex } from 'readable-stream';\n\nimport type { BaseProviderOptions } from './BaseProvider';\nimport { BaseProvider } from './BaseProvider';\nimport messages from './messages';\nimport {\n EMITTED_NOTIFICATIONS,\n isValidChainId,\n isValidNetworkVersion,\n} from './utils';\n\nexport type StreamProviderOptions = BaseProviderOptions;\n\nexport type JsonRpcConnection = {\n events: SafeEventEmitter;\n middleware: JsonRpcMiddleware;\n stream: Duplex;\n};\n\n/**\n * An abstract EIP-1193 provider wired to some duplex stream via a\n * `json-rpc-middleware-stream` JSON-RPC stream middleware. Implementers must\n * call {@link AbstractStreamProvider._initializeStateAsync} after instantiation\n * to initialize the provider's state.\n */\nexport abstract class AbstractStreamProvider extends BaseProvider {\n protected _jsonRpcConnection: JsonRpcConnection;\n\n /**\n * Creates a new AbstractStreamProvider instance.\n *\n * @param connectionStream - A Node.js duplex stream.\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.rpcMiddleware - The RPC middleware stack to use.\n */\n constructor(\n connectionStream: Duplex,\n {\n logger = console,\n maxEventListeners = 100,\n rpcMiddleware = [],\n }: StreamProviderOptions = {},\n ) {\n super({ logger, maxEventListeners, rpcMiddleware });\n\n if (!isDuplex(connectionStream)) {\n throw new Error(messages.errors.invalidDuplexStream());\n }\n\n // Bind functions to prevent consumers from making unbound calls\n this._handleStreamDisconnect = this._handleStreamDisconnect.bind(this);\n\n // Set up RPC connection\n // Typecast: The type of `Duplex` is incompatible with the type of\n // `JsonRpcConnection`.\n this._jsonRpcConnection = createStreamMiddleware({\n retryOnMessage: 'METAMASK_EXTENSION_CONNECT_CAN_RETRY',\n }) as unknown as JsonRpcConnection;\n\n pipeline(\n connectionStream,\n this._jsonRpcConnection.stream,\n connectionStream,\n this._handleStreamDisconnect.bind(this, 'MetaMask RpcProvider'),\n );\n\n // Wire up the JsonRpcEngine to the JSON-RPC connection stream\n this._rpcEngine.push(this._jsonRpcConnection.middleware);\n\n // Handle JSON-RPC notifications\n this._jsonRpcConnection.events.on('notification', (payload) => {\n const { method, params } = payload;\n if (method === 'metamask_accountsChanged') {\n this._handleAccountsChanged(params);\n } else if (method === 'metamask_unlockStateChanged') {\n this._handleUnlockStateChanged(params);\n } else if (method === 'metamask_chainChanged') {\n this._handleChainChanged(params);\n } else if (EMITTED_NOTIFICATIONS.includes(method)) {\n this.emit('message', {\n type: method,\n data: params,\n });\n } else if (method === 'METAMASK_STREAM_FAILURE') {\n connectionStream.destroy(\n new Error(messages.errors.permanentlyDisconnected()),\n );\n }\n });\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * MUST be called by child classes.\n *\n * Calls `metamask_getProviderState` and passes the result to\n * {@link BaseProvider._initializeState}. Logs an error if getting initial state\n * fails. Throws if called after initialization has completed.\n */\n protected async _initializeStateAsync() {\n let initialState: Parameters[0];\n\n try {\n initialState = (await this.request({\n method: 'metamask_getProviderState',\n })) as Parameters[0];\n } catch (error) {\n this._log.error(\n 'MetaMask: Failed to get initial state. Please report this bug.',\n error,\n );\n }\n this._initializeState(initialState);\n }\n\n /**\n * Called when connection is lost to critical streams. Emits an 'error' event\n * from the provider with the error message and stack if present.\n *\n * @param streamName - The name of the stream that disconnected.\n * @param error - The error that caused the disconnection.\n * @fires BaseProvider#disconnect - If the provider is not already\n * disconnected.\n */\n // eslint-disable-next-line no-restricted-syntax\n private _handleStreamDisconnect(streamName: string, error: Error | null) {\n let warningMsg = `MetaMask: Lost connection to \"${streamName}\".`;\n if (error?.stack) {\n warningMsg += `\\n${error.stack}`;\n }\n\n this._log.warn(warningMsg);\n if (this.listenerCount('error') > 0) {\n this.emit('error', warningMsg);\n }\n\n this._handleDisconnect(false, error ? error.message : undefined);\n }\n\n /**\n * Upon receipt of a new chainId and networkVersion, emits corresponding\n * events and sets relevant public state. This class does not have a\n * `networkVersion` property, but we rely on receiving a `networkVersion`\n * with the value of `loading` to detect when the network is changing and\n * a recoverable `disconnect` even has occurred. Child classes that use the\n * `networkVersion` for other purposes must implement additional handling\n * therefore.\n *\n * @fires BaseProvider#chainChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n * @param networkInfo.networkVersion - The latest network ID.\n */\n protected _handleChainChanged({\n chainId,\n networkVersion,\n }: {\n chainId?: string | undefined;\n networkVersion?: string | undefined;\n } = {}) {\n if (!isValidChainId(chainId) || !isValidNetworkVersion(networkVersion)) {\n this._log.error(messages.errors.invalidNetworkParams(), {\n chainId,\n networkVersion,\n });\n return;\n }\n\n if (networkVersion === 'loading') {\n this._handleDisconnect(true);\n } else {\n super._handleChainChanged({ chainId });\n }\n }\n}\n\n/**\n * An EIP-1193 provider wired to some duplex stream via a\n * `json-rpc-middleware-stream` JSON-RPC stream middleware. Consumers must\n * call {@link StreamProvider.initialize} after instantiation to complete\n * initialization.\n */\nexport class StreamProvider extends AbstractStreamProvider {\n /**\n * MUST be called after instantiation to complete initialization.\n *\n * Calls `metamask_getProviderState` and passes the result to\n * {@link BaseProvider._initializeState}. Logs an error if getting initial state\n * fails. Throws if called after initialization has completed.\n */\n async initialize() {\n return this._initializeStateAsync();\n }\n}\n"]} -\ No newline at end of file -+{"version":3,"file":"StreamProvider.cjs","sourceRoot":"","sources":["../src/StreamProvider.ts"],"names":[],"mappings":";;;;;;AACA,qFAA8E;AAG9E,kDAA+C;AAC/C,qDAA2C;AAI3C,qDAA8C;AAC9C,8DAAkC;AAClC,uCAIiB;AAUjB;;;;;GAKG;AACH,MAAsB,sBAAuB,SAAQ,2BAAY;IAG/D;;;;;;;;;OASG;IACH,YACE,gBAAwB,EACxB,EACE,MAAM,GAAG,OAAO,EAChB,iBAAiB,GAAG,GAAG,EACvB,aAAa,GAAG,EAAE,MACO,EAAE;QAE7B,KAAK,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,aAAa,EAAE,CAAC,CAAC;QAEpD,IAAI,CAAC,IAAA,kBAAQ,EAAC,gBAAgB,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,kBAAQ,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC;QACzD,CAAC;QAED,gEAAgE;QAChE,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEvE,wBAAwB;QACxB,kEAAkE;QAClE,uBAAuB;QACvB,IAAI,CAAC,kBAAkB,GAAG,IAAA,mDAAsB,EAAC;YAC/C,cAAc,EAAE,sCAAsC;SACvD,CAAiC,CAAC;QAEnC,IAAA,0BAAQ,EACN,gBAAgB,EAChB,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAC9B,gBAAgB,EAChB,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAChE,CAAC;QAEF,8DAA8D;QAC9D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAEzD,gCAAgC;QAChC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,OAAO,EAAE,EAAE;YAC5D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;YACnC,IAAI,MAAM,KAAK,0BAA0B,EAAE,CAAC;gBAC1C,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC;YACtC,CAAC;iBAAM,IAAI,MAAM,KAAK,6BAA6B,EAAE,CAAC;gBACpD,IAAI,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC;YACzC,CAAC;iBAAM,IAAI,MAAM,KAAK,uBAAuB,EAAE,CAAC;gBAC9C,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;YACnC,CAAC;iBAAM,IAAI,6BAAqB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;oBACnB,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,MAAM;iBACb,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,MAAM,KAAK,yBAAyB,EAAE,CAAC;gBAChD,gBAAgB,CAAC,OAAO,CACtB,IAAI,KAAK,CAAC,kBAAQ,CAAC,MAAM,CAAC,uBAAuB,EAAE,CAAC,CACrD,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,sBAAsB;IACtB,kBAAkB;IAClB,sBAAsB;IAEtB;;;;;;OAMG;IACO,KAAK,CAAC,qBAAqB;QACnC,IAAI,YAA6D,CAAC;QAElE,IAAI,CAAC;YACH,YAAY,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC;gBACjC,MAAM,EAAE,2BAA2B;aACpC,CAAC,CAAoD,CAAC;QACzD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,KAAK,CACb,gEAAgE,EAChE,KAAK,CACN,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;;OAQG;IACH,gDAAgD;IACxC,uBAAuB,CAAC,UAAkB,EAAE,KAAmB;QACrE,IAAI,UAAU,GAAG,iCAAiC,UAAU,IAAI,CAAC;QACjE,IAAI,KAAK,EAAE,KAAK,EAAE,CAAC;YACjB,UAAU,IAAI,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC;QACnC,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3B,IAAI,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QACjC,CAAC;QAED,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACnE,CAAC;IAED;;;;;;;;;;OAUG;IACO,mBAAmB,CAAC,EAC5B,OAAO,EACP,cAAc,EACd,WAAW,MAKT,EAAE;QACJ,IAAI,CAAC,IAAA,sBAAc,EAAC,OAAO,CAAC,IAAI,CAAC,IAAA,6BAAqB,EAAC,cAAc,CAAC,EAAE,CAAC;YACvE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAQ,CAAC,MAAM,CAAC,oBAAoB,EAAE,EAAE;gBACtD,OAAO;gBACP,cAAc;aACf,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,KAAK,CAAC,mBAAmB,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QAEpD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;CACF;AA1JD,wDA0JC;AAED;;;;;GAKG;AACH,MAAa,cAAe,SAAQ,sBAAsB;IACxD;;;;;;OAMG;IACH,KAAK,CAAC,UAAU;QACd,OAAO,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACtC,CAAC;CACF;AAXD,wCAWC","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createStreamMiddleware } from '@metamask/json-rpc-middleware-stream';\nimport type SafeEventEmitter from '@metamask/safe-event-emitter';\nimport type { Json, JsonRpcParams } from '@metamask/utils';\nimport { duplex as isDuplex } from 'is-stream';\nimport { pipeline } from 'readable-stream';\nimport type { Duplex } from 'readable-stream';\n\nimport type { BaseProviderOptions } from './BaseProvider';\nimport { BaseProvider } from './BaseProvider';\nimport messages from './messages';\nimport {\n EMITTED_NOTIFICATIONS,\n isValidChainId,\n isValidNetworkVersion,\n} from './utils';\n\nexport type StreamProviderOptions = BaseProviderOptions;\n\nexport type JsonRpcConnection = {\n events: SafeEventEmitter;\n middleware: JsonRpcMiddleware;\n stream: Duplex;\n};\n\n/**\n * An abstract EIP-1193 provider wired to some duplex stream via a\n * `json-rpc-middleware-stream` JSON-RPC stream middleware. Implementers must\n * call {@link AbstractStreamProvider._initializeStateAsync} after instantiation\n * to initialize the provider's state.\n */\nexport abstract class AbstractStreamProvider extends BaseProvider {\n protected _jsonRpcConnection: JsonRpcConnection;\n\n /**\n * Creates a new AbstractStreamProvider instance.\n *\n * @param connectionStream - A Node.js duplex stream.\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.rpcMiddleware - The RPC middleware stack to use.\n */\n constructor(\n connectionStream: Duplex,\n {\n logger = console,\n maxEventListeners = 100,\n rpcMiddleware = [],\n }: StreamProviderOptions = {},\n ) {\n super({ logger, maxEventListeners, rpcMiddleware });\n\n if (!isDuplex(connectionStream)) {\n throw new Error(messages.errors.invalidDuplexStream());\n }\n\n // Bind functions to prevent consumers from making unbound calls\n this._handleStreamDisconnect = this._handleStreamDisconnect.bind(this);\n\n // Set up RPC connection\n // Typecast: The type of `Duplex` is incompatible with the type of\n // `JsonRpcConnection`.\n this._jsonRpcConnection = createStreamMiddleware({\n retryOnMessage: 'METAMASK_EXTENSION_CONNECT_CAN_RETRY',\n }) as unknown as JsonRpcConnection;\n\n pipeline(\n connectionStream,\n this._jsonRpcConnection.stream,\n connectionStream,\n this._handleStreamDisconnect.bind(this, 'MetaMask RpcProvider'),\n );\n\n // Wire up the JsonRpcEngine to the JSON-RPC connection stream\n this._rpcEngine.push(this._jsonRpcConnection.middleware);\n\n // Handle JSON-RPC notifications\n this._jsonRpcConnection.events.on('notification', (payload) => {\n const { method, params } = payload;\n if (method === 'metamask_accountsChanged') {\n this._handleAccountsChanged(params);\n } else if (method === 'metamask_unlockStateChanged') {\n this._handleUnlockStateChanged(params);\n } else if (method === 'metamask_chainChanged') {\n this._handleChainChanged(params);\n } else if (EMITTED_NOTIFICATIONS.includes(method)) {\n this.emit('message', {\n type: method,\n data: params,\n });\n } else if (method === 'METAMASK_STREAM_FAILURE') {\n connectionStream.destroy(\n new Error(messages.errors.permanentlyDisconnected()),\n );\n }\n });\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * MUST be called by child classes.\n *\n * Calls `metamask_getProviderState` and passes the result to\n * {@link BaseProvider._initializeState}. Logs an error if getting initial state\n * fails. Throws if called after initialization has completed.\n */\n protected async _initializeStateAsync() {\n let initialState: Parameters[0];\n\n try {\n initialState = (await this.request({\n method: 'metamask_getProviderState',\n })) as Parameters[0];\n } catch (error) {\n this._log.error(\n 'MetaMask: Failed to get initial state. Please report this bug.',\n error,\n );\n }\n this._initializeState(initialState);\n }\n\n /**\n * Called when connection is lost to critical streams. Emits an 'error' event\n * from the provider with the error message and stack if present.\n *\n * @param streamName - The name of the stream that disconnected.\n * @param error - The error that caused the disconnection.\n * @fires BaseProvider#disconnect - If the provider is not already\n * disconnected.\n */\n // eslint-disable-next-line no-restricted-syntax\n private _handleStreamDisconnect(streamName: string, error: Error | null) {\n let warningMsg = `MetaMask: Lost connection to \"${streamName}\".`;\n if (error?.stack) {\n warningMsg += `\\n${error.stack}`;\n }\n\n this._log.warn(warningMsg);\n if (this.listenerCount('error') > 0) {\n this.emit('error', warningMsg);\n }\n\n this._handleDisconnect(false, error ? error.message : undefined);\n }\n\n /**\n * Upon receipt of a new chainId and networkVersion, emits corresponding\n * events and sets relevant public state. Child classes that use the\n * `networkVersion` for other purposes must implement additional handling.\n *\n * @fires BaseProvider#chainChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n * @param networkInfo.networkVersion - The latest network ID.\n * @param networkInfo.isConnected - Whether the network is available.\n */\n protected _handleChainChanged({\n chainId,\n networkVersion,\n isConnected,\n }: {\n chainId?: string | undefined;\n networkVersion?: string | undefined;\n isConnected?: boolean | undefined;\n } = {}) {\n if (!isValidChainId(chainId) || !isValidNetworkVersion(networkVersion)) {\n this._log.error(messages.errors.invalidNetworkParams(), {\n chainId,\n networkVersion,\n });\n return;\n }\n\n super._handleChainChanged({ chainId, isConnected });\n\n if (!isConnected) {\n this._handleDisconnect(true);\n }\n }\n}\n\n/**\n * An EIP-1193 provider wired to some duplex stream via a\n * `json-rpc-middleware-stream` JSON-RPC stream middleware. Consumers must\n * call {@link StreamProvider.initialize} after instantiation to complete\n * initialization.\n */\nexport class StreamProvider extends AbstractStreamProvider {\n /**\n * MUST be called after instantiation to complete initialization.\n *\n * Calls `metamask_getProviderState` and passes the result to\n * {@link BaseProvider._initializeState}. Logs an error if getting initial state\n * fails. Throws if called after initialization has completed.\n */\n async initialize() {\n return this._initializeStateAsync();\n }\n}\n"]} -\ No newline at end of file -diff --git a/dist/StreamProvider.d.cts b/dist/StreamProvider.d.cts -index 33aec7331989045ccb35f04907430fa72a4eef4f..a9b266b53030ee9194f4ba82ee12ef0b1272dbcd 100644 ---- a/dist/StreamProvider.d.cts -+++ b/dist/StreamProvider.d.cts -@@ -49,21 +49,19 @@ export declare abstract class AbstractStreamProvider extends BaseProvider { - private _handleStreamDisconnect; - /** - * Upon receipt of a new chainId and networkVersion, emits corresponding -- * events and sets relevant public state. This class does not have a -- * `networkVersion` property, but we rely on receiving a `networkVersion` -- * with the value of `loading` to detect when the network is changing and -- * a recoverable `disconnect` even has occurred. Child classes that use the -- * `networkVersion` for other purposes must implement additional handling -- * therefore. -+ * events and sets relevant public state. Child classes that use the -+ * `networkVersion` for other purposes must implement additional handling. - * - * @fires BaseProvider#chainChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. - * @param networkInfo.networkVersion - The latest network ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- protected _handleChainChanged({ chainId, networkVersion, }?: { -+ protected _handleChainChanged({ chainId, networkVersion, isConnected, }?: { - chainId?: string | undefined; - networkVersion?: string | undefined; -+ isConnected?: boolean | undefined; - }): void; - } - /** -diff --git a/dist/StreamProvider.d.cts.map b/dist/StreamProvider.d.cts.map -index be38744b9ad52c4ada34b6e64ebbc009e3ecd11f..680bf589305bb4b4064534113ddd3ff7bdbedf25 100644 ---- a/dist/StreamProvider.d.cts.map -+++ b/dist/StreamProvider.d.cts.map -@@ -1 +1 @@ --{"version":3,"file":"StreamProvider.d.cts","sourceRoot":"","sources":["../src/StreamProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAEnE,OAAO,KAAK,gBAAgB,qCAAqC;AACjE,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,wBAAwB;AAG3D,OAAO,KAAK,EAAE,MAAM,EAAE,wBAAwB;AAE9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,2BAAuB;AAC1D,OAAO,EAAE,YAAY,EAAE,2BAAuB;AAQ9C,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAExD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,8BAAsB,sBAAuB,SAAQ,YAAY;IAC/D,SAAS,CAAC,kBAAkB,EAAE,iBAAiB,CAAC;IAEhD;;;;;;;;;OASG;gBAED,gBAAgB,EAAE,MAAM,EACxB,EACE,MAAgB,EAChB,iBAAuB,EACvB,aAAkB,GACnB,GAAE,qBAA0B;IAsD/B;;;;;;OAMG;cACa,qBAAqB;IAgBrC;;;;;;;;OAQG;IAEH,OAAO,CAAC,uBAAuB;IAc/B;;;;;;;;;;;;;OAaG;IACH,SAAS,CAAC,mBAAmB,CAAC,EAC5B,OAAO,EACP,cAAc,GACf,GAAE;QACD,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAChC;CAeP;AAED;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,sBAAsB;IACxD;;;;;;OAMG;IACG,UAAU;CAGjB"} -\ No newline at end of file -+{"version":3,"file":"StreamProvider.d.cts","sourceRoot":"","sources":["../src/StreamProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAEnE,OAAO,KAAK,gBAAgB,qCAAqC;AACjE,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,wBAAwB;AAG3D,OAAO,KAAK,EAAE,MAAM,EAAE,wBAAwB;AAE9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,2BAAuB;AAC1D,OAAO,EAAE,YAAY,EAAE,2BAAuB;AAQ9C,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAExD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,8BAAsB,sBAAuB,SAAQ,YAAY;IAC/D,SAAS,CAAC,kBAAkB,EAAE,iBAAiB,CAAC;IAEhD;;;;;;;;;OASG;gBAED,gBAAgB,EAAE,MAAM,EACxB,EACE,MAAgB,EAChB,iBAAuB,EACvB,aAAkB,GACnB,GAAE,qBAA0B;IAsD/B;;;;;;OAMG;cACa,qBAAqB;IAgBrC;;;;;;;;OAQG;IAEH,OAAO,CAAC,uBAAuB;IAc/B;;;;;;;;;;OAUG;IACH,SAAS,CAAC,mBAAmB,CAAC,EAC5B,OAAO,EACP,cAAc,EACd,WAAW,GACZ,GAAE;QACD,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACpC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KAC9B;CAeP;AAED;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,sBAAsB;IACxD;;;;;;OAMG;IACG,UAAU;CAGjB"} -\ No newline at end of file -diff --git a/dist/StreamProvider.d.mts b/dist/StreamProvider.d.mts -index a7d9f50393d600a6c97829e95a4721c2ae7c6fec..062b347d33a235183ee1a91c3497395e51839c8b 100644 ---- a/dist/StreamProvider.d.mts -+++ b/dist/StreamProvider.d.mts -@@ -49,21 +49,19 @@ export declare abstract class AbstractStreamProvider extends BaseProvider { - private _handleStreamDisconnect; - /** - * Upon receipt of a new chainId and networkVersion, emits corresponding -- * events and sets relevant public state. This class does not have a -- * `networkVersion` property, but we rely on receiving a `networkVersion` -- * with the value of `loading` to detect when the network is changing and -- * a recoverable `disconnect` even has occurred. Child classes that use the -- * `networkVersion` for other purposes must implement additional handling -- * therefore. -+ * events and sets relevant public state. Child classes that use the -+ * `networkVersion` for other purposes must implement additional handling. - * - * @fires BaseProvider#chainChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. - * @param networkInfo.networkVersion - The latest network ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- protected _handleChainChanged({ chainId, networkVersion, }?: { -+ protected _handleChainChanged({ chainId, networkVersion, isConnected, }?: { - chainId?: string | undefined; - networkVersion?: string | undefined; -+ isConnected?: boolean | undefined; - }): void; - } - /** -diff --git a/dist/StreamProvider.d.mts.map b/dist/StreamProvider.d.mts.map -index 582a8b2fe46b1d075ddd8832b5c9fa447f050cc9..b8b11aca0af0fd65657d93b8d427c0f291e803bb 100644 ---- a/dist/StreamProvider.d.mts.map -+++ b/dist/StreamProvider.d.mts.map -@@ -1 +1 @@ --{"version":3,"file":"StreamProvider.d.mts","sourceRoot":"","sources":["../src/StreamProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAEnE,OAAO,KAAK,gBAAgB,qCAAqC;AACjE,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,wBAAwB;AAG3D,OAAO,KAAK,EAAE,MAAM,EAAE,wBAAwB;AAE9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,2BAAuB;AAC1D,OAAO,EAAE,YAAY,EAAE,2BAAuB;AAQ9C,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAExD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,8BAAsB,sBAAuB,SAAQ,YAAY;IAC/D,SAAS,CAAC,kBAAkB,EAAE,iBAAiB,CAAC;IAEhD;;;;;;;;;OASG;gBAED,gBAAgB,EAAE,MAAM,EACxB,EACE,MAAgB,EAChB,iBAAuB,EACvB,aAAkB,GACnB,GAAE,qBAA0B;IAsD/B;;;;;;OAMG;cACa,qBAAqB;IAgBrC;;;;;;;;OAQG;IAEH,OAAO,CAAC,uBAAuB;IAc/B;;;;;;;;;;;;;OAaG;IACH,SAAS,CAAC,mBAAmB,CAAC,EAC5B,OAAO,EACP,cAAc,GACf,GAAE;QACD,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAChC;CAeP;AAED;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,sBAAsB;IACxD;;;;;;OAMG;IACG,UAAU;CAGjB"} -\ No newline at end of file -+{"version":3,"file":"StreamProvider.d.mts","sourceRoot":"","sources":["../src/StreamProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAEnE,OAAO,KAAK,gBAAgB,qCAAqC;AACjE,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,wBAAwB;AAG3D,OAAO,KAAK,EAAE,MAAM,EAAE,wBAAwB;AAE9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,2BAAuB;AAC1D,OAAO,EAAE,YAAY,EAAE,2BAAuB;AAQ9C,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAExD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,8BAAsB,sBAAuB,SAAQ,YAAY;IAC/D,SAAS,CAAC,kBAAkB,EAAE,iBAAiB,CAAC;IAEhD;;;;;;;;;OASG;gBAED,gBAAgB,EAAE,MAAM,EACxB,EACE,MAAgB,EAChB,iBAAuB,EACvB,aAAkB,GACnB,GAAE,qBAA0B;IAsD/B;;;;;;OAMG;cACa,qBAAqB;IAgBrC;;;;;;;;OAQG;IAEH,OAAO,CAAC,uBAAuB;IAc/B;;;;;;;;;;OAUG;IACH,SAAS,CAAC,mBAAmB,CAAC,EAC5B,OAAO,EACP,cAAc,EACd,WAAW,GACZ,GAAE;QACD,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACpC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KAC9B;CAeP;AAED;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,sBAAsB;IACxD;;;;;;OAMG;IACG,UAAU;CAGjB"} -\ No newline at end of file -diff --git a/dist/StreamProvider.mjs b/dist/StreamProvider.mjs -index d120836b4d98cc29d94da2b352f96df228a9c843..76c56fd6344b89d105ff9e8581b4121b33167295 100644 ---- a/dist/StreamProvider.mjs -+++ b/dist/StreamProvider.mjs -@@ -106,19 +106,16 @@ export class AbstractStreamProvider extends BaseProvider { - } - /** - * Upon receipt of a new chainId and networkVersion, emits corresponding -- * events and sets relevant public state. This class does not have a -- * `networkVersion` property, but we rely on receiving a `networkVersion` -- * with the value of `loading` to detect when the network is changing and -- * a recoverable `disconnect` even has occurred. Child classes that use the -- * `networkVersion` for other purposes must implement additional handling -- * therefore. -+ * events and sets relevant public state. Child classes that use the -+ * `networkVersion` for other purposes must implement additional handling. - * - * @fires BaseProvider#chainChanged - * @param networkInfo - An object with network info. - * @param networkInfo.chainId - The latest chain ID. - * @param networkInfo.networkVersion - The latest network ID. -+ * @param networkInfo.isConnected - Whether the network is available. - */ -- _handleChainChanged({ chainId, networkVersion, } = {}) { -+ _handleChainChanged({ chainId, networkVersion, isConnected, } = {}) { - if (!isValidChainId(chainId) || !isValidNetworkVersion(networkVersion)) { - this._log.error(messages.errors.invalidNetworkParams(), { - chainId, -@@ -126,12 +123,10 @@ export class AbstractStreamProvider extends BaseProvider { - }); - return; - } -- if (networkVersion === 'loading') { -+ super._handleChainChanged({ chainId, isConnected }); -+ if (!isConnected) { - this._handleDisconnect(true); - } -- else { -- super._handleChainChanged({ chainId }); -- } - } - } - /** -diff --git a/dist/StreamProvider.mjs.map b/dist/StreamProvider.mjs.map -index 858710e3a4d523ab86c58f9828a08fef7b7cdbcb..6c734a5b54eef43ec01dd31d156056ad17f0b280 100644 ---- a/dist/StreamProvider.mjs.map -+++ b/dist/StreamProvider.mjs.map -@@ -1 +1 @@ --{"version":3,"file":"StreamProvider.mjs","sourceRoot":"","sources":["../src/StreamProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createStreamMiddleware } from '@metamask/json-rpc-middleware-stream';\nimport type SafeEventEmitter from '@metamask/safe-event-emitter';\nimport type { Json, JsonRpcParams } from '@metamask/utils';\nimport { duplex as isDuplex } from 'is-stream';\nimport { pipeline } from 'readable-stream';\nimport type { Duplex } from 'readable-stream';\n\nimport type { BaseProviderOptions } from './BaseProvider';\nimport { BaseProvider } from './BaseProvider';\nimport messages from './messages';\nimport {\n EMITTED_NOTIFICATIONS,\n isValidChainId,\n isValidNetworkVersion,\n} from './utils';\n\nexport type StreamProviderOptions = BaseProviderOptions;\n\nexport type JsonRpcConnection = {\n events: SafeEventEmitter;\n middleware: JsonRpcMiddleware;\n stream: Duplex;\n};\n\n/**\n * An abstract EIP-1193 provider wired to some duplex stream via a\n * `json-rpc-middleware-stream` JSON-RPC stream middleware. Implementers must\n * call {@link AbstractStreamProvider._initializeStateAsync} after instantiation\n * to initialize the provider's state.\n */\nexport abstract class AbstractStreamProvider extends BaseProvider {\n protected _jsonRpcConnection: JsonRpcConnection;\n\n /**\n * Creates a new AbstractStreamProvider instance.\n *\n * @param connectionStream - A Node.js duplex stream.\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.rpcMiddleware - The RPC middleware stack to use.\n */\n constructor(\n connectionStream: Duplex,\n {\n logger = console,\n maxEventListeners = 100,\n rpcMiddleware = [],\n }: StreamProviderOptions = {},\n ) {\n super({ logger, maxEventListeners, rpcMiddleware });\n\n if (!isDuplex(connectionStream)) {\n throw new Error(messages.errors.invalidDuplexStream());\n }\n\n // Bind functions to prevent consumers from making unbound calls\n this._handleStreamDisconnect = this._handleStreamDisconnect.bind(this);\n\n // Set up RPC connection\n // Typecast: The type of `Duplex` is incompatible with the type of\n // `JsonRpcConnection`.\n this._jsonRpcConnection = createStreamMiddleware({\n retryOnMessage: 'METAMASK_EXTENSION_CONNECT_CAN_RETRY',\n }) as unknown as JsonRpcConnection;\n\n pipeline(\n connectionStream,\n this._jsonRpcConnection.stream,\n connectionStream,\n this._handleStreamDisconnect.bind(this, 'MetaMask RpcProvider'),\n );\n\n // Wire up the JsonRpcEngine to the JSON-RPC connection stream\n this._rpcEngine.push(this._jsonRpcConnection.middleware);\n\n // Handle JSON-RPC notifications\n this._jsonRpcConnection.events.on('notification', (payload) => {\n const { method, params } = payload;\n if (method === 'metamask_accountsChanged') {\n this._handleAccountsChanged(params);\n } else if (method === 'metamask_unlockStateChanged') {\n this._handleUnlockStateChanged(params);\n } else if (method === 'metamask_chainChanged') {\n this._handleChainChanged(params);\n } else if (EMITTED_NOTIFICATIONS.includes(method)) {\n this.emit('message', {\n type: method,\n data: params,\n });\n } else if (method === 'METAMASK_STREAM_FAILURE') {\n connectionStream.destroy(\n new Error(messages.errors.permanentlyDisconnected()),\n );\n }\n });\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * MUST be called by child classes.\n *\n * Calls `metamask_getProviderState` and passes the result to\n * {@link BaseProvider._initializeState}. Logs an error if getting initial state\n * fails. Throws if called after initialization has completed.\n */\n protected async _initializeStateAsync() {\n let initialState: Parameters[0];\n\n try {\n initialState = (await this.request({\n method: 'metamask_getProviderState',\n })) as Parameters[0];\n } catch (error) {\n this._log.error(\n 'MetaMask: Failed to get initial state. Please report this bug.',\n error,\n );\n }\n this._initializeState(initialState);\n }\n\n /**\n * Called when connection is lost to critical streams. Emits an 'error' event\n * from the provider with the error message and stack if present.\n *\n * @param streamName - The name of the stream that disconnected.\n * @param error - The error that caused the disconnection.\n * @fires BaseProvider#disconnect - If the provider is not already\n * disconnected.\n */\n // eslint-disable-next-line no-restricted-syntax\n private _handleStreamDisconnect(streamName: string, error: Error | null) {\n let warningMsg = `MetaMask: Lost connection to \"${streamName}\".`;\n if (error?.stack) {\n warningMsg += `\\n${error.stack}`;\n }\n\n this._log.warn(warningMsg);\n if (this.listenerCount('error') > 0) {\n this.emit('error', warningMsg);\n }\n\n this._handleDisconnect(false, error ? error.message : undefined);\n }\n\n /**\n * Upon receipt of a new chainId and networkVersion, emits corresponding\n * events and sets relevant public state. This class does not have a\n * `networkVersion` property, but we rely on receiving a `networkVersion`\n * with the value of `loading` to detect when the network is changing and\n * a recoverable `disconnect` even has occurred. Child classes that use the\n * `networkVersion` for other purposes must implement additional handling\n * therefore.\n *\n * @fires BaseProvider#chainChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n * @param networkInfo.networkVersion - The latest network ID.\n */\n protected _handleChainChanged({\n chainId,\n networkVersion,\n }: {\n chainId?: string | undefined;\n networkVersion?: string | undefined;\n } = {}) {\n if (!isValidChainId(chainId) || !isValidNetworkVersion(networkVersion)) {\n this._log.error(messages.errors.invalidNetworkParams(), {\n chainId,\n networkVersion,\n });\n return;\n }\n\n if (networkVersion === 'loading') {\n this._handleDisconnect(true);\n } else {\n super._handleChainChanged({ chainId });\n }\n }\n}\n\n/**\n * An EIP-1193 provider wired to some duplex stream via a\n * `json-rpc-middleware-stream` JSON-RPC stream middleware. Consumers must\n * call {@link StreamProvider.initialize} after instantiation to complete\n * initialization.\n */\nexport class StreamProvider extends AbstractStreamProvider {\n /**\n * MUST be called after instantiation to complete initialization.\n *\n * Calls `metamask_getProviderState` and passes the result to\n * {@link BaseProvider._initializeState}. Logs an error if getting initial state\n * fails. Throws if called after initialization has completed.\n */\n async initialize() {\n return this._initializeStateAsync();\n }\n}\n"]} -\ No newline at end of file -+{"version":3,"file":"StreamProvider.mjs","sourceRoot":"","sources":["../src/StreamProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createStreamMiddleware } from '@metamask/json-rpc-middleware-stream';\nimport type SafeEventEmitter from '@metamask/safe-event-emitter';\nimport type { Json, JsonRpcParams } from '@metamask/utils';\nimport { duplex as isDuplex } from 'is-stream';\nimport { pipeline } from 'readable-stream';\nimport type { Duplex } from 'readable-stream';\n\nimport type { BaseProviderOptions } from './BaseProvider';\nimport { BaseProvider } from './BaseProvider';\nimport messages from './messages';\nimport {\n EMITTED_NOTIFICATIONS,\n isValidChainId,\n isValidNetworkVersion,\n} from './utils';\n\nexport type StreamProviderOptions = BaseProviderOptions;\n\nexport type JsonRpcConnection = {\n events: SafeEventEmitter;\n middleware: JsonRpcMiddleware;\n stream: Duplex;\n};\n\n/**\n * An abstract EIP-1193 provider wired to some duplex stream via a\n * `json-rpc-middleware-stream` JSON-RPC stream middleware. Implementers must\n * call {@link AbstractStreamProvider._initializeStateAsync} after instantiation\n * to initialize the provider's state.\n */\nexport abstract class AbstractStreamProvider extends BaseProvider {\n protected _jsonRpcConnection: JsonRpcConnection;\n\n /**\n * Creates a new AbstractStreamProvider instance.\n *\n * @param connectionStream - A Node.js duplex stream.\n * @param options - An options bag.\n * @param options.logger - The logging API to use. Default: `console`.\n * @param options.maxEventListeners - The maximum number of event\n * listeners. Default: 100.\n * @param options.rpcMiddleware - The RPC middleware stack to use.\n */\n constructor(\n connectionStream: Duplex,\n {\n logger = console,\n maxEventListeners = 100,\n rpcMiddleware = [],\n }: StreamProviderOptions = {},\n ) {\n super({ logger, maxEventListeners, rpcMiddleware });\n\n if (!isDuplex(connectionStream)) {\n throw new Error(messages.errors.invalidDuplexStream());\n }\n\n // Bind functions to prevent consumers from making unbound calls\n this._handleStreamDisconnect = this._handleStreamDisconnect.bind(this);\n\n // Set up RPC connection\n // Typecast: The type of `Duplex` is incompatible with the type of\n // `JsonRpcConnection`.\n this._jsonRpcConnection = createStreamMiddleware({\n retryOnMessage: 'METAMASK_EXTENSION_CONNECT_CAN_RETRY',\n }) as unknown as JsonRpcConnection;\n\n pipeline(\n connectionStream,\n this._jsonRpcConnection.stream,\n connectionStream,\n this._handleStreamDisconnect.bind(this, 'MetaMask RpcProvider'),\n );\n\n // Wire up the JsonRpcEngine to the JSON-RPC connection stream\n this._rpcEngine.push(this._jsonRpcConnection.middleware);\n\n // Handle JSON-RPC notifications\n this._jsonRpcConnection.events.on('notification', (payload) => {\n const { method, params } = payload;\n if (method === 'metamask_accountsChanged') {\n this._handleAccountsChanged(params);\n } else if (method === 'metamask_unlockStateChanged') {\n this._handleUnlockStateChanged(params);\n } else if (method === 'metamask_chainChanged') {\n this._handleChainChanged(params);\n } else if (EMITTED_NOTIFICATIONS.includes(method)) {\n this.emit('message', {\n type: method,\n data: params,\n });\n } else if (method === 'METAMASK_STREAM_FAILURE') {\n connectionStream.destroy(\n new Error(messages.errors.permanentlyDisconnected()),\n );\n }\n });\n }\n\n //====================\n // Private Methods\n //====================\n\n /**\n * MUST be called by child classes.\n *\n * Calls `metamask_getProviderState` and passes the result to\n * {@link BaseProvider._initializeState}. Logs an error if getting initial state\n * fails. Throws if called after initialization has completed.\n */\n protected async _initializeStateAsync() {\n let initialState: Parameters[0];\n\n try {\n initialState = (await this.request({\n method: 'metamask_getProviderState',\n })) as Parameters[0];\n } catch (error) {\n this._log.error(\n 'MetaMask: Failed to get initial state. Please report this bug.',\n error,\n );\n }\n this._initializeState(initialState);\n }\n\n /**\n * Called when connection is lost to critical streams. Emits an 'error' event\n * from the provider with the error message and stack if present.\n *\n * @param streamName - The name of the stream that disconnected.\n * @param error - The error that caused the disconnection.\n * @fires BaseProvider#disconnect - If the provider is not already\n * disconnected.\n */\n // eslint-disable-next-line no-restricted-syntax\n private _handleStreamDisconnect(streamName: string, error: Error | null) {\n let warningMsg = `MetaMask: Lost connection to \"${streamName}\".`;\n if (error?.stack) {\n warningMsg += `\\n${error.stack}`;\n }\n\n this._log.warn(warningMsg);\n if (this.listenerCount('error') > 0) {\n this.emit('error', warningMsg);\n }\n\n this._handleDisconnect(false, error ? error.message : undefined);\n }\n\n /**\n * Upon receipt of a new chainId and networkVersion, emits corresponding\n * events and sets relevant public state. Child classes that use the\n * `networkVersion` for other purposes must implement additional handling.\n *\n * @fires BaseProvider#chainChanged\n * @param networkInfo - An object with network info.\n * @param networkInfo.chainId - The latest chain ID.\n * @param networkInfo.networkVersion - The latest network ID.\n * @param networkInfo.isConnected - Whether the network is available.\n */\n protected _handleChainChanged({\n chainId,\n networkVersion,\n isConnected,\n }: {\n chainId?: string | undefined;\n networkVersion?: string | undefined;\n isConnected?: boolean | undefined;\n } = {}) {\n if (!isValidChainId(chainId) || !isValidNetworkVersion(networkVersion)) {\n this._log.error(messages.errors.invalidNetworkParams(), {\n chainId,\n networkVersion,\n });\n return;\n }\n\n super._handleChainChanged({ chainId, isConnected });\n\n if (!isConnected) {\n this._handleDisconnect(true);\n }\n }\n}\n\n/**\n * An EIP-1193 provider wired to some duplex stream via a\n * `json-rpc-middleware-stream` JSON-RPC stream middleware. Consumers must\n * call {@link StreamProvider.initialize} after instantiation to complete\n * initialization.\n */\nexport class StreamProvider extends AbstractStreamProvider {\n /**\n * MUST be called after instantiation to complete initialization.\n *\n * Calls `metamask_getProviderState` and passes the result to\n * {@link BaseProvider._initializeState}. Logs an error if getting initial state\n * fails. Throws if called after initialization has completed.\n */\n async initialize() {\n return this._initializeStateAsync();\n }\n}\n"]} -\ No newline at end of file -diff --git a/dist/utils.cjs b/dist/utils.cjs -index e8d637dce7aeddd253a8f409fa43216fe922364d..65a06560707b527a3f2c711d205d2f9307060abf 100644 ---- a/dist/utils.cjs -+++ b/dist/utils.cjs -@@ -9,6 +9,8 @@ const createRpcWarningMiddleware_1 = require("./middleware/createRpcWarningMiddl - exports.UUID_V4_REGEX = /(?:^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}$)|(?:^0{8}-0{4}-0{4}-0{4}-0{12}$)/u; - // https://stackoverflow.com/a/20204811 - exports.FQDN_REGEX = /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)/u; -+// https://stackoverflow.com/a/9523559 -+const POSITIVE_INTEGER_REGEX = /^(\d*[1-9]\d*|0)$/u; - exports.EMITTED_NOTIFICATIONS = Object.freeze([ - 'eth_subscription', // per eth-json-rpc-filters/subscriptionManager - ]); -@@ -75,12 +77,13 @@ const isValidChainId = (chainId) => Boolean(chainId) && typeof chainId === 'stri - exports.isValidChainId = isValidChainId; - /** - * Checks whether the given network version is valid, meaning if it is non-empty -- * string. -+ * integer string or the value 'loading'. - * - * @param networkVersion - The network version to validate. - * @returns Whether the given network version is valid. - */ --const isValidNetworkVersion = (networkVersion) => Boolean(networkVersion) && typeof networkVersion === 'string'; -+const isValidNetworkVersion = (networkVersion) => typeof networkVersion === 'string' && -+ (POSITIVE_INTEGER_REGEX.test(networkVersion) || networkVersion === 'loading'); - exports.isValidNetworkVersion = isValidNetworkVersion; - const NOOP = () => undefined; - exports.NOOP = NOOP; -diff --git a/dist/utils.cjs.map b/dist/utils.cjs.map -index b1396454a57ebdeeaf483b71ca9cea49fd6f9e05..53f30e974ab8340858c5aaa7e464738747126927 100644 ---- a/dist/utils.cjs.map -+++ b/dist/utils.cjs.map -@@ -1 +1 @@ --{"version":3,"file":"utils.cjs","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AACA,+DAAoE;AACpE,qDAAiD;AAOjD,4FAAqF;AASrF,YAAY;AAEZ,wGAAwG;AAC3F,QAAA,aAAa,GACxB,sGAAsG,CAAC;AAEzG,uCAAuC;AAC1B,QAAA,UAAU,GACrB,0EAA0E,CAAC;AAEhE,QAAA,qBAAqB,GAAG,MAAM,CAAC,MAAM,CAAC;IACjD,kBAAkB,EAAE,+CAA+C;CACpE,CAAC,CAAC;AAEH,oBAAoB;AAEpB;;;;;;GAMG;AACI,MAAM,4BAA4B,GAAG,CAAC,SAAsB,OAAO,EAAE,EAAE,CAAC;IAC7E,IAAA,yCAAuB,GAAE;IACzB,qBAAqB,CAAC,MAAM,CAAC;IAC7B,IAAA,uDAA0B,EAAC,MAAM,CAAC;CACnC,CAAC;AAJW,QAAA,4BAA4B,gCAIvC;AAEF;;;;;;GAMG;AACH,SAAS,qBAAqB,CAC5B,GAAgB;IAEhB,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;QACjC,wEAAwE;QACxE,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1D,QAAQ,CAAC,KAAK,GAAG,sBAAS,CAAC,cAAc,CAAC;gBACxC,OAAO,EAAE,kDAAkD;gBAC3D,IAAI,EAAE,OAAO;aACd,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YACZ,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC;YAC3B,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,IAAI,EAAE,CAAC;YAChB,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;YAC1D,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,qDAAqD;AAC9C,MAAM,qBAAqB,GAChC,CACE,OAA8B,EAC9B,MAA+B,EAC/B,YAAY,GAAG,IAAI,EACnB,EAAE,CACJ,CAAC,KAAY,EAAE,QAAsC,EAAQ,EAAE;IAC7D,IAAI,KAAK,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,CAAC,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;YACtC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;YACnB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC,CAAC;AAdS,QAAA,qBAAqB,yBAc9B;AAEJ;;;;;;GAMG;AACI,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAqB,EAAE,CACpE,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AADjE,QAAA,cAAc,kBACmD;AAE9E;;;;;;GAMG;AACI,MAAM,qBAAqB,GAAG,CACnC,cAAuB,EACG,EAAE,CAC5B,OAAO,CAAC,cAAc,CAAC,IAAI,OAAO,cAAc,KAAK,QAAQ,CAAC;AAHnD,QAAA,qBAAqB,yBAG8B;AAEzD,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC;AAAvB,QAAA,IAAI,QAAmB","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createIdRemapMiddleware } from '@metamask/json-rpc-engine';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type {\n Json,\n JsonRpcParams,\n PendingJsonRpcResponse,\n} from '@metamask/utils';\n\nimport { createRpcWarningMiddleware } from './middleware/createRpcWarningMiddleware';\n\nexport type Maybe = Partial | null | undefined;\n\nexport type ConsoleLike = Pick<\n Console,\n 'log' | 'warn' | 'error' | 'debug' | 'info' | 'trace'\n>;\n\n// Constants\n\n// https://github.com/thenativeweb/uuidv4/blob/bdcf3a3138bef4fb7c51f389a170666f9012c478/lib/uuidv4.ts#L5\nexport const UUID_V4_REGEX =\n /(?:^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}$)|(?:^0{8}-0{4}-0{4}-0{4}-0{12}$)/u;\n\n// https://stackoverflow.com/a/20204811\nexport const FQDN_REGEX =\n /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\\.)+[a-zA-Z]{2,63}$)/u;\n\nexport const EMITTED_NOTIFICATIONS = Object.freeze([\n 'eth_subscription', // per eth-json-rpc-filters/subscriptionManager\n]);\n\n// Utility functions\n\n/**\n * Gets the default middleware for external providers, consisting of an ID\n * remapping middleware and an error middleware.\n *\n * @param logger - The logger to use in the error middleware.\n * @returns An array of @metamask/json-rpc-engine middleware functions.\n */\nexport const getDefaultExternalMiddleware = (logger: ConsoleLike = console) => [\n createIdRemapMiddleware(),\n createErrorMiddleware(logger),\n createRpcWarningMiddleware(logger),\n];\n\n/**\n * A `json-rpc-engine` middleware that logs RPC errors and validates the request\n * method.\n *\n * @param log - The logging API to use.\n * @returns A @metamask/json-rpc-engine middleware function.\n */\nfunction createErrorMiddleware(\n log: ConsoleLike,\n): JsonRpcMiddleware {\n return (request, response, next) => {\n // json-rpc-engine will terminate the request when it notices this error\n if (typeof request.method !== 'string' || !request.method) {\n response.error = rpcErrors.invalidRequest({\n message: `The request 'method' must be a non-empty string.`,\n data: request,\n });\n }\n\n next((done) => {\n const { error } = response;\n if (!error) {\n return done();\n }\n log.warn(`MetaMask - RPC Error: ${error.message}`, error);\n return done();\n });\n };\n}\n\n// resolve response.result or response, reject errors\nexport const getRpcPromiseCallback =\n (\n resolve: (value?: any) => void,\n reject: (error?: Error) => void,\n unwrapResult = true,\n ) =>\n (error: Error, response: PendingJsonRpcResponse): void => {\n if (error || response.error) {\n reject(error || response.error);\n } else {\n !unwrapResult || Array.isArray(response)\n ? resolve(response)\n : resolve(response.result);\n }\n };\n\n/**\n * Checks whether the given chain ID is valid, meaning if it is non-empty,\n * '0x'-prefixed string.\n *\n * @param chainId - The chain ID to validate.\n * @returns Whether the given chain ID is valid.\n */\nexport const isValidChainId = (chainId: unknown): chainId is string =>\n Boolean(chainId) && typeof chainId === 'string' && chainId.startsWith('0x');\n\n/**\n * Checks whether the given network version is valid, meaning if it is non-empty\n * string.\n *\n * @param networkVersion - The network version to validate.\n * @returns Whether the given network version is valid.\n */\nexport const isValidNetworkVersion = (\n networkVersion: unknown,\n): networkVersion is string =>\n Boolean(networkVersion) && typeof networkVersion === 'string';\n\nexport const NOOP = () => undefined;\n"]} -\ No newline at end of file -+{"version":3,"file":"utils.cjs","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AACA,+DAAoE;AACpE,qDAAiD;AAOjD,4FAAqF;AASrF,YAAY;AAEZ,wGAAwG;AAC3F,QAAA,aAAa,GACxB,sGAAsG,CAAC;AAEzG,uCAAuC;AAC1B,QAAA,UAAU,GACrB,0EAA0E,CAAC;AAE7E,sCAAsC;AACtC,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAEvC,QAAA,qBAAqB,GAAG,MAAM,CAAC,MAAM,CAAC;IACjD,kBAAkB,EAAE,+CAA+C;CACpE,CAAC,CAAC;AAEH,oBAAoB;AAEpB;;;;;;GAMG;AACI,MAAM,4BAA4B,GAAG,CAAC,SAAsB,OAAO,EAAE,EAAE,CAAC;IAC7E,IAAA,yCAAuB,GAAE;IACzB,qBAAqB,CAAC,MAAM,CAAC;IAC7B,IAAA,uDAA0B,EAAC,MAAM,CAAC;CACnC,CAAC;AAJW,QAAA,4BAA4B,gCAIvC;AAEF;;;;;;GAMG;AACH,SAAS,qBAAqB,CAC5B,GAAgB;IAEhB,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;QACjC,wEAAwE;QACxE,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1D,QAAQ,CAAC,KAAK,GAAG,sBAAS,CAAC,cAAc,CAAC;gBACxC,OAAO,EAAE,kDAAkD;gBAC3D,IAAI,EAAE,OAAO;aACd,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YACZ,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC;YAC3B,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,IAAI,EAAE,CAAC;YAChB,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;YAC1D,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,qDAAqD;AAC9C,MAAM,qBAAqB,GAChC,CACE,OAA8B,EAC9B,MAA+B,EAC/B,YAAY,GAAG,IAAI,EACnB,EAAE,CACJ,CAAC,KAAY,EAAE,QAAsC,EAAQ,EAAE;IAC7D,IAAI,KAAK,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,CAAC,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;YACtC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;YACnB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC,CAAC;AAdS,QAAA,qBAAqB,yBAc9B;AAEJ;;;;;;GAMG;AACI,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAqB,EAAE,CACpE,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AADjE,QAAA,cAAc,kBACmD;AAE9E;;;;;;GAMG;AACI,MAAM,qBAAqB,GAAG,CACnC,cAAuB,EACU,EAAE,CACnC,OAAO,cAAc,KAAK,QAAQ;IAClC,CAAC,sBAAsB,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,cAAc,KAAK,SAAS,CAAC,CAAC;AAJnE,QAAA,qBAAqB,yBAI8C;AAEzE,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC;AAAvB,QAAA,IAAI,QAAmB","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createIdRemapMiddleware } from '@metamask/json-rpc-engine';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type {\n Json,\n JsonRpcParams,\n PendingJsonRpcResponse,\n} from '@metamask/utils';\n\nimport { createRpcWarningMiddleware } from './middleware/createRpcWarningMiddleware';\n\nexport type Maybe = Partial | null | undefined;\n\nexport type ConsoleLike = Pick<\n Console,\n 'log' | 'warn' | 'error' | 'debug' | 'info' | 'trace'\n>;\n\n// Constants\n\n// https://github.com/thenativeweb/uuidv4/blob/bdcf3a3138bef4fb7c51f389a170666f9012c478/lib/uuidv4.ts#L5\nexport const UUID_V4_REGEX =\n /(?:^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}$)|(?:^0{8}-0{4}-0{4}-0{4}-0{12}$)/u;\n\n// https://stackoverflow.com/a/20204811\nexport const FQDN_REGEX =\n /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\\.)+[a-zA-Z]{2,63}$)/u;\n\n// https://stackoverflow.com/a/9523559\nconst POSITIVE_INTEGER_REGEX = /^(\\d*[1-9]\\d*|0)$/u;\n\nexport const EMITTED_NOTIFICATIONS = Object.freeze([\n 'eth_subscription', // per eth-json-rpc-filters/subscriptionManager\n]);\n\n// Utility functions\n\n/**\n * Gets the default middleware for external providers, consisting of an ID\n * remapping middleware and an error middleware.\n *\n * @param logger - The logger to use in the error middleware.\n * @returns An array of @metamask/json-rpc-engine middleware functions.\n */\nexport const getDefaultExternalMiddleware = (logger: ConsoleLike = console) => [\n createIdRemapMiddleware(),\n createErrorMiddleware(logger),\n createRpcWarningMiddleware(logger),\n];\n\n/**\n * A `json-rpc-engine` middleware that logs RPC errors and validates the request\n * method.\n *\n * @param log - The logging API to use.\n * @returns A @metamask/json-rpc-engine middleware function.\n */\nfunction createErrorMiddleware(\n log: ConsoleLike,\n): JsonRpcMiddleware {\n return (request, response, next) => {\n // json-rpc-engine will terminate the request when it notices this error\n if (typeof request.method !== 'string' || !request.method) {\n response.error = rpcErrors.invalidRequest({\n message: `The request 'method' must be a non-empty string.`,\n data: request,\n });\n }\n\n next((done) => {\n const { error } = response;\n if (!error) {\n return done();\n }\n log.warn(`MetaMask - RPC Error: ${error.message}`, error);\n return done();\n });\n };\n}\n\n// resolve response.result or response, reject errors\nexport const getRpcPromiseCallback =\n (\n resolve: (value?: any) => void,\n reject: (error?: Error) => void,\n unwrapResult = true,\n ) =>\n (error: Error, response: PendingJsonRpcResponse): void => {\n if (error || response.error) {\n reject(error || response.error);\n } else {\n !unwrapResult || Array.isArray(response)\n ? resolve(response)\n : resolve(response.result);\n }\n };\n\n/**\n * Checks whether the given chain ID is valid, meaning if it is non-empty,\n * '0x'-prefixed string.\n *\n * @param chainId - The chain ID to validate.\n * @returns Whether the given chain ID is valid.\n */\nexport const isValidChainId = (chainId: unknown): chainId is string =>\n Boolean(chainId) && typeof chainId === 'string' && chainId.startsWith('0x');\n\n/**\n * Checks whether the given network version is valid, meaning if it is non-empty\n * integer string or the value 'loading'.\n *\n * @param networkVersion - The network version to validate.\n * @returns Whether the given network version is valid.\n */\nexport const isValidNetworkVersion = (\n networkVersion: unknown,\n): networkVersion is string | null =>\n typeof networkVersion === 'string' &&\n (POSITIVE_INTEGER_REGEX.test(networkVersion) || networkVersion === 'loading');\n\nexport const NOOP = () => undefined;\n"]} -\ No newline at end of file -diff --git a/dist/utils.d.cts b/dist/utils.d.cts -index a1cdeb51719058c35cf3b257f6879680eadb6856..f0b64f034858c490af80b7d9c571de1e9af5ac9a 100644 ---- a/dist/utils.d.cts -+++ b/dist/utils.d.cts -@@ -24,11 +24,11 @@ export declare const getRpcPromiseCallback: (resolve: (value?: any) => void, rej - export declare const isValidChainId: (chainId: unknown) => chainId is string; - /** - * Checks whether the given network version is valid, meaning if it is non-empty -- * string. -+ * integer string or the value 'loading'. - * - * @param networkVersion - The network version to validate. - * @returns Whether the given network version is valid. - */ --export declare const isValidNetworkVersion: (networkVersion: unknown) => networkVersion is string; -+export declare const isValidNetworkVersion: (networkVersion: unknown) => networkVersion is string | null; - export declare const NOOP: () => undefined; - //# sourceMappingURL=utils.d.cts.map -\ No newline at end of file -diff --git a/dist/utils.d.cts.map b/dist/utils.d.cts.map -index 9aaac725656815fe8c2c2771c476ac4f6ba5b8eb..a4ff36c93f73ebb6462a634a740416cb559a61a5 100644 ---- a/dist/utils.d.cts.map -+++ b/dist/utils.d.cts.map -@@ -1 +1 @@ --{"version":3,"file":"utils.d.cts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAGnE,OAAO,KAAK,EACV,IAAI,EACJ,aAAa,EACb,sBAAsB,EACvB,wBAAwB;AAIzB,MAAM,MAAM,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAE3D,MAAM,MAAM,WAAW,GAAG,IAAI,CAC5B,OAAO,EACP,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CACtD,CAAC;AAKF,eAAO,MAAM,aAAa,QAC8E,CAAC;AAGzG,eAAO,MAAM,UAAU,QACqD,CAAC;AAE7E,eAAO,MAAM,qBAAqB,mBAEhC,CAAC;AAIH;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,YAAY,WAAW,6CAI/D,CAAC;AAiCF,eAAO,MAAM,qBAAqB,YAErB,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,UACtB,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,qCAGzB,KAAK,YAAY,sBAAsB,CAAC,IAAI,CAAC,KAAG,IAQvD,CAAC;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,YAAa,OAAO,KAAG,OAAO,IAAI,MACgB,CAAC;AAE9E;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,mBAChB,OAAO,KACtB,cAAc,IAAI,MAC0C,CAAC;AAEhE,eAAO,MAAM,IAAI,iBAAkB,CAAC"} -\ No newline at end of file -+{"version":3,"file":"utils.d.cts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAGnE,OAAO,KAAK,EACV,IAAI,EACJ,aAAa,EACb,sBAAsB,EACvB,wBAAwB;AAIzB,MAAM,MAAM,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAE3D,MAAM,MAAM,WAAW,GAAG,IAAI,CAC5B,OAAO,EACP,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CACtD,CAAC;AAKF,eAAO,MAAM,aAAa,QAC8E,CAAC;AAGzG,eAAO,MAAM,UAAU,QACqD,CAAC;AAK7E,eAAO,MAAM,qBAAqB,mBAEhC,CAAC;AAIH;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,YAAY,WAAW,6CAI/D,CAAC;AAiCF,eAAO,MAAM,qBAAqB,YAErB,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,UACtB,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,qCAGzB,KAAK,YAAY,sBAAsB,CAAC,IAAI,CAAC,KAAG,IAQvD,CAAC;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,YAAa,OAAO,KAAG,OAAO,IAAI,MACgB,CAAC;AAE9E;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,mBAChB,OAAO,KACtB,cAAc,IAAI,MAAM,GAAG,IAEiD,CAAC;AAEhF,eAAO,MAAM,IAAI,iBAAkB,CAAC"} -\ No newline at end of file -diff --git a/dist/utils.d.mts b/dist/utils.d.mts -index 9001ed0963fdb2bffdc5c0497b0c91763e34f4db..440a3d167f10a5c1b562a0782f7e011a5450145a 100644 ---- a/dist/utils.d.mts -+++ b/dist/utils.d.mts -@@ -24,11 +24,11 @@ export declare const getRpcPromiseCallback: (resolve: (value?: any) => void, rej - export declare const isValidChainId: (chainId: unknown) => chainId is string; - /** - * Checks whether the given network version is valid, meaning if it is non-empty -- * string. -+ * integer string or the value 'loading'. - * - * @param networkVersion - The network version to validate. - * @returns Whether the given network version is valid. - */ --export declare const isValidNetworkVersion: (networkVersion: unknown) => networkVersion is string; -+export declare const isValidNetworkVersion: (networkVersion: unknown) => networkVersion is string | null; - export declare const NOOP: () => undefined; - //# sourceMappingURL=utils.d.mts.map -\ No newline at end of file -diff --git a/dist/utils.d.mts.map b/dist/utils.d.mts.map -index e10cdda420846efccb6f52d150da2902ace49dc8..0e4d50501892b26213d362470d76e027035a67fd 100644 ---- a/dist/utils.d.mts.map -+++ b/dist/utils.d.mts.map -@@ -1 +1 @@ --{"version":3,"file":"utils.d.mts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAGnE,OAAO,KAAK,EACV,IAAI,EACJ,aAAa,EACb,sBAAsB,EACvB,wBAAwB;AAIzB,MAAM,MAAM,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAE3D,MAAM,MAAM,WAAW,GAAG,IAAI,CAC5B,OAAO,EACP,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CACtD,CAAC;AAKF,eAAO,MAAM,aAAa,QAC8E,CAAC;AAGzG,eAAO,MAAM,UAAU,QACqD,CAAC;AAE7E,eAAO,MAAM,qBAAqB,mBAEhC,CAAC;AAIH;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,YAAY,WAAW,6CAI/D,CAAC;AAiCF,eAAO,MAAM,qBAAqB,YAErB,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,UACtB,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,qCAGzB,KAAK,YAAY,sBAAsB,CAAC,IAAI,CAAC,KAAG,IAQvD,CAAC;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,YAAa,OAAO,KAAG,OAAO,IAAI,MACgB,CAAC;AAE9E;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,mBAChB,OAAO,KACtB,cAAc,IAAI,MAC0C,CAAC;AAEhE,eAAO,MAAM,IAAI,iBAAkB,CAAC"} -\ No newline at end of file -+{"version":3,"file":"utils.d.mts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kCAAkC;AAGnE,OAAO,KAAK,EACV,IAAI,EACJ,aAAa,EACb,sBAAsB,EACvB,wBAAwB;AAIzB,MAAM,MAAM,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAE3D,MAAM,MAAM,WAAW,GAAG,IAAI,CAC5B,OAAO,EACP,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CACtD,CAAC;AAKF,eAAO,MAAM,aAAa,QAC8E,CAAC;AAGzG,eAAO,MAAM,UAAU,QACqD,CAAC;AAK7E,eAAO,MAAM,qBAAqB,mBAEhC,CAAC;AAIH;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,YAAY,WAAW,6CAI/D,CAAC;AAiCF,eAAO,MAAM,qBAAqB,YAErB,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,UACtB,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,qCAGzB,KAAK,YAAY,sBAAsB,CAAC,IAAI,CAAC,KAAG,IAQvD,CAAC;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,YAAa,OAAO,KAAG,OAAO,IAAI,MACgB,CAAC;AAE9E;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,mBAChB,OAAO,KACtB,cAAc,IAAI,MAAM,GAAG,IAEiD,CAAC;AAEhF,eAAO,MAAM,IAAI,iBAAkB,CAAC"} -\ No newline at end of file -diff --git a/dist/utils.mjs b/dist/utils.mjs -index bb0299702a637dc539820e123382966ecf2c0e8e..623f1ce4c3f252312ad3ada4ee08f865c1f58a9b 100644 ---- a/dist/utils.mjs -+++ b/dist/utils.mjs -@@ -6,6 +6,8 @@ import { createRpcWarningMiddleware } from "./middleware/createRpcWarningMiddlew - export const UUID_V4_REGEX = /(?:^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}$)|(?:^0{8}-0{4}-0{4}-0{4}-0{12}$)/u; - // https://stackoverflow.com/a/20204811 - export const FQDN_REGEX = /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)/u; -+// https://stackoverflow.com/a/9523559 -+const POSITIVE_INTEGER_REGEX = /^(\d*[1-9]\d*|0)$/u; - export const EMITTED_NOTIFICATIONS = Object.freeze([ - 'eth_subscription', // per eth-json-rpc-filters/subscriptionManager - ]); -@@ -69,11 +71,12 @@ export const getRpcPromiseCallback = (resolve, reject, unwrapResult = true) => ( - export const isValidChainId = (chainId) => Boolean(chainId) && typeof chainId === 'string' && chainId.startsWith('0x'); - /** - * Checks whether the given network version is valid, meaning if it is non-empty -- * string. -+ * integer string or the value 'loading'. - * - * @param networkVersion - The network version to validate. - * @returns Whether the given network version is valid. - */ --export const isValidNetworkVersion = (networkVersion) => Boolean(networkVersion) && typeof networkVersion === 'string'; -+export const isValidNetworkVersion = (networkVersion) => typeof networkVersion === 'string' && -+ (POSITIVE_INTEGER_REGEX.test(networkVersion) || networkVersion === 'loading'); - export const NOOP = () => undefined; - //# sourceMappingURL=utils.mjs.map -\ No newline at end of file -diff --git a/dist/utils.mjs.map b/dist/utils.mjs.map -index a9e9357bb46464fbea2641cb9fd12a74fb048000..ba7e940beaa247458f945c16794760a2c40f0669 100644 ---- a/dist/utils.mjs.map -+++ b/dist/utils.mjs.map -@@ -1 +1 @@ --{"version":3,"file":"utils.mjs","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,kCAAkC;AACpE,OAAO,EAAE,SAAS,EAAE,6BAA6B;AAOjD,OAAO,EAAE,0BAA0B,EAAE,oDAAgD;AASrF,YAAY;AAEZ,wGAAwG;AACxG,MAAM,CAAC,MAAM,aAAa,GACxB,sGAAsG,CAAC;AAEzG,uCAAuC;AACvC,MAAM,CAAC,MAAM,UAAU,GACrB,0EAA0E,CAAC;AAE7E,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,CAAC;IACjD,kBAAkB,EAAE,+CAA+C;CACpE,CAAC,CAAC;AAEH,oBAAoB;AAEpB;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,SAAsB,OAAO,EAAE,EAAE,CAAC;IAC7E,uBAAuB,EAAE;IACzB,qBAAqB,CAAC,MAAM,CAAC;IAC7B,0BAA0B,CAAC,MAAM,CAAC;CACnC,CAAC;AAEF;;;;;;GAMG;AACH,SAAS,qBAAqB,CAC5B,GAAgB;IAEhB,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;QACjC,wEAAwE;QACxE,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1D,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAC,cAAc,CAAC;gBACxC,OAAO,EAAE,kDAAkD;gBAC3D,IAAI,EAAE,OAAO;aACd,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YACZ,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC;YAC3B,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,IAAI,EAAE,CAAC;YAChB,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;YAC1D,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,qDAAqD;AACrD,MAAM,CAAC,MAAM,qBAAqB,GAChC,CACE,OAA8B,EAC9B,MAA+B,EAC/B,YAAY,GAAG,IAAI,EACnB,EAAE,CACJ,CAAC,KAAY,EAAE,QAAsC,EAAQ,EAAE;IAC7D,IAAI,KAAK,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,CAAC,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;YACtC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;YACnB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC,CAAC;AAEJ;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAqB,EAAE,CACpE,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,cAAuB,EACG,EAAE,CAC5B,OAAO,CAAC,cAAc,CAAC,IAAI,OAAO,cAAc,KAAK,QAAQ,CAAC;AAEhE,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createIdRemapMiddleware } from '@metamask/json-rpc-engine';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type {\n Json,\n JsonRpcParams,\n PendingJsonRpcResponse,\n} from '@metamask/utils';\n\nimport { createRpcWarningMiddleware } from './middleware/createRpcWarningMiddleware';\n\nexport type Maybe = Partial | null | undefined;\n\nexport type ConsoleLike = Pick<\n Console,\n 'log' | 'warn' | 'error' | 'debug' | 'info' | 'trace'\n>;\n\n// Constants\n\n// https://github.com/thenativeweb/uuidv4/blob/bdcf3a3138bef4fb7c51f389a170666f9012c478/lib/uuidv4.ts#L5\nexport const UUID_V4_REGEX =\n /(?:^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}$)|(?:^0{8}-0{4}-0{4}-0{4}-0{12}$)/u;\n\n// https://stackoverflow.com/a/20204811\nexport const FQDN_REGEX =\n /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\\.)+[a-zA-Z]{2,63}$)/u;\n\nexport const EMITTED_NOTIFICATIONS = Object.freeze([\n 'eth_subscription', // per eth-json-rpc-filters/subscriptionManager\n]);\n\n// Utility functions\n\n/**\n * Gets the default middleware for external providers, consisting of an ID\n * remapping middleware and an error middleware.\n *\n * @param logger - The logger to use in the error middleware.\n * @returns An array of @metamask/json-rpc-engine middleware functions.\n */\nexport const getDefaultExternalMiddleware = (logger: ConsoleLike = console) => [\n createIdRemapMiddleware(),\n createErrorMiddleware(logger),\n createRpcWarningMiddleware(logger),\n];\n\n/**\n * A `json-rpc-engine` middleware that logs RPC errors and validates the request\n * method.\n *\n * @param log - The logging API to use.\n * @returns A @metamask/json-rpc-engine middleware function.\n */\nfunction createErrorMiddleware(\n log: ConsoleLike,\n): JsonRpcMiddleware {\n return (request, response, next) => {\n // json-rpc-engine will terminate the request when it notices this error\n if (typeof request.method !== 'string' || !request.method) {\n response.error = rpcErrors.invalidRequest({\n message: `The request 'method' must be a non-empty string.`,\n data: request,\n });\n }\n\n next((done) => {\n const { error } = response;\n if (!error) {\n return done();\n }\n log.warn(`MetaMask - RPC Error: ${error.message}`, error);\n return done();\n });\n };\n}\n\n// resolve response.result or response, reject errors\nexport const getRpcPromiseCallback =\n (\n resolve: (value?: any) => void,\n reject: (error?: Error) => void,\n unwrapResult = true,\n ) =>\n (error: Error, response: PendingJsonRpcResponse): void => {\n if (error || response.error) {\n reject(error || response.error);\n } else {\n !unwrapResult || Array.isArray(response)\n ? resolve(response)\n : resolve(response.result);\n }\n };\n\n/**\n * Checks whether the given chain ID is valid, meaning if it is non-empty,\n * '0x'-prefixed string.\n *\n * @param chainId - The chain ID to validate.\n * @returns Whether the given chain ID is valid.\n */\nexport const isValidChainId = (chainId: unknown): chainId is string =>\n Boolean(chainId) && typeof chainId === 'string' && chainId.startsWith('0x');\n\n/**\n * Checks whether the given network version is valid, meaning if it is non-empty\n * string.\n *\n * @param networkVersion - The network version to validate.\n * @returns Whether the given network version is valid.\n */\nexport const isValidNetworkVersion = (\n networkVersion: unknown,\n): networkVersion is string =>\n Boolean(networkVersion) && typeof networkVersion === 'string';\n\nexport const NOOP = () => undefined;\n"]} -\ No newline at end of file -+{"version":3,"file":"utils.mjs","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,kCAAkC;AACpE,OAAO,EAAE,SAAS,EAAE,6BAA6B;AAOjD,OAAO,EAAE,0BAA0B,EAAE,oDAAgD;AASrF,YAAY;AAEZ,wGAAwG;AACxG,MAAM,CAAC,MAAM,aAAa,GACxB,sGAAsG,CAAC;AAEzG,uCAAuC;AACvC,MAAM,CAAC,MAAM,UAAU,GACrB,0EAA0E,CAAC;AAE7E,sCAAsC;AACtC,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAEpD,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,CAAC;IACjD,kBAAkB,EAAE,+CAA+C;CACpE,CAAC,CAAC;AAEH,oBAAoB;AAEpB;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,SAAsB,OAAO,EAAE,EAAE,CAAC;IAC7E,uBAAuB,EAAE;IACzB,qBAAqB,CAAC,MAAM,CAAC;IAC7B,0BAA0B,CAAC,MAAM,CAAC;CACnC,CAAC;AAEF;;;;;;GAMG;AACH,SAAS,qBAAqB,CAC5B,GAAgB;IAEhB,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;QACjC,wEAAwE;QACxE,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1D,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAC,cAAc,CAAC;gBACxC,OAAO,EAAE,kDAAkD;gBAC3D,IAAI,EAAE,OAAO;aACd,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YACZ,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC;YAC3B,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,IAAI,EAAE,CAAC;YAChB,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;YAC1D,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,qDAAqD;AACrD,MAAM,CAAC,MAAM,qBAAqB,GAChC,CACE,OAA8B,EAC9B,MAA+B,EAC/B,YAAY,GAAG,IAAI,EACnB,EAAE,CACJ,CAAC,KAAY,EAAE,QAAsC,EAAQ,EAAE;IAC7D,IAAI,KAAK,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,CAAC,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;YACtC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;YACnB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC,CAAC;AAEJ;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAqB,EAAE,CACpE,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,cAAuB,EACU,EAAE,CACnC,OAAO,cAAc,KAAK,QAAQ;IAClC,CAAC,sBAAsB,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,cAAc,KAAK,SAAS,CAAC,CAAC;AAEhF,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC","sourcesContent":["import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine';\nimport { createIdRemapMiddleware } from '@metamask/json-rpc-engine';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type {\n Json,\n JsonRpcParams,\n PendingJsonRpcResponse,\n} from '@metamask/utils';\n\nimport { createRpcWarningMiddleware } from './middleware/createRpcWarningMiddleware';\n\nexport type Maybe = Partial | null | undefined;\n\nexport type ConsoleLike = Pick<\n Console,\n 'log' | 'warn' | 'error' | 'debug' | 'info' | 'trace'\n>;\n\n// Constants\n\n// https://github.com/thenativeweb/uuidv4/blob/bdcf3a3138bef4fb7c51f389a170666f9012c478/lib/uuidv4.ts#L5\nexport const UUID_V4_REGEX =\n /(?:^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}$)|(?:^0{8}-0{4}-0{4}-0{4}-0{12}$)/u;\n\n// https://stackoverflow.com/a/20204811\nexport const FQDN_REGEX =\n /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\\.)+[a-zA-Z]{2,63}$)/u;\n\n// https://stackoverflow.com/a/9523559\nconst POSITIVE_INTEGER_REGEX = /^(\\d*[1-9]\\d*|0)$/u;\n\nexport const EMITTED_NOTIFICATIONS = Object.freeze([\n 'eth_subscription', // per eth-json-rpc-filters/subscriptionManager\n]);\n\n// Utility functions\n\n/**\n * Gets the default middleware for external providers, consisting of an ID\n * remapping middleware and an error middleware.\n *\n * @param logger - The logger to use in the error middleware.\n * @returns An array of @metamask/json-rpc-engine middleware functions.\n */\nexport const getDefaultExternalMiddleware = (logger: ConsoleLike = console) => [\n createIdRemapMiddleware(),\n createErrorMiddleware(logger),\n createRpcWarningMiddleware(logger),\n];\n\n/**\n * A `json-rpc-engine` middleware that logs RPC errors and validates the request\n * method.\n *\n * @param log - The logging API to use.\n * @returns A @metamask/json-rpc-engine middleware function.\n */\nfunction createErrorMiddleware(\n log: ConsoleLike,\n): JsonRpcMiddleware {\n return (request, response, next) => {\n // json-rpc-engine will terminate the request when it notices this error\n if (typeof request.method !== 'string' || !request.method) {\n response.error = rpcErrors.invalidRequest({\n message: `The request 'method' must be a non-empty string.`,\n data: request,\n });\n }\n\n next((done) => {\n const { error } = response;\n if (!error) {\n return done();\n }\n log.warn(`MetaMask - RPC Error: ${error.message}`, error);\n return done();\n });\n };\n}\n\n// resolve response.result or response, reject errors\nexport const getRpcPromiseCallback =\n (\n resolve: (value?: any) => void,\n reject: (error?: Error) => void,\n unwrapResult = true,\n ) =>\n (error: Error, response: PendingJsonRpcResponse): void => {\n if (error || response.error) {\n reject(error || response.error);\n } else {\n !unwrapResult || Array.isArray(response)\n ? resolve(response)\n : resolve(response.result);\n }\n };\n\n/**\n * Checks whether the given chain ID is valid, meaning if it is non-empty,\n * '0x'-prefixed string.\n *\n * @param chainId - The chain ID to validate.\n * @returns Whether the given chain ID is valid.\n */\nexport const isValidChainId = (chainId: unknown): chainId is string =>\n Boolean(chainId) && typeof chainId === 'string' && chainId.startsWith('0x');\n\n/**\n * Checks whether the given network version is valid, meaning if it is non-empty\n * integer string or the value 'loading'.\n *\n * @param networkVersion - The network version to validate.\n * @returns Whether the given network version is valid.\n */\nexport const isValidNetworkVersion = (\n networkVersion: unknown,\n): networkVersion is string | null =>\n typeof networkVersion === 'string' &&\n (POSITIVE_INTEGER_REGEX.test(networkVersion) || networkVersion === 'loading');\n\nexport const NOOP = () => undefined;\n"]} -\ No newline at end of file diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index a390d1d0b04c..0eb802250fbb 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -4775,13 +4775,13 @@ "message": "Cash out with MetaMask" }, "slideDebitCardDescription": { - "message": "Available in selected regions" + "message": "Available in select regions" }, "slideDebitCardTitle": { "message": "MetaMask debit card" }, "slideFundWalletDescription": { - "message": "Get started by adding funds" + "message": "Add or transfer tokens to get started" }, "slideFundWalletTitle": { "message": "Fund your wallet" diff --git a/app/_locales/en_GB/messages.json b/app/_locales/en_GB/messages.json index a390d1d0b04c..0eb802250fbb 100644 --- a/app/_locales/en_GB/messages.json +++ b/app/_locales/en_GB/messages.json @@ -4775,13 +4775,13 @@ "message": "Cash out with MetaMask" }, "slideDebitCardDescription": { - "message": "Available in selected regions" + "message": "Available in select regions" }, "slideDebitCardTitle": { "message": "MetaMask debit card" }, "slideFundWalletDescription": { - "message": "Get started by adding funds" + "message": "Add or transfer tokens to get started" }, "slideFundWalletTitle": { "message": "Fund your wallet" diff --git a/app/images/unichain.svg b/app/images/unichain.svg new file mode 100644 index 000000000000..22f8225772a8 --- /dev/null +++ b/app/images/unichain.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/app/scripts/constants/sentry-state.ts b/app/scripts/constants/sentry-state.ts index 2d8154557065..5a632e39df50 100644 --- a/app/scripts/constants/sentry-state.ts +++ b/app/scripts/constants/sentry-state.ts @@ -105,6 +105,9 @@ export const SENTRY_BACKGROUND_STATE = { accountsAssets: false, assetsMetadata: false, }, + MultiChainAssetsRatesController: { + assetsRates: false, + }, BridgeController: { bridgeState: { bridgeFeatureFlags: { diff --git a/app/scripts/controller-init/controller-list.ts b/app/scripts/controller-init/controller-list.ts index c0ea242e8c31..e7ffdf8588e4 100644 --- a/app/scripts/controller-init/controller-list.ts +++ b/app/scripts/controller-init/controller-list.ts @@ -13,8 +13,10 @@ import { TransactionUpdateController } from '@metamask-institutional/transaction import { AccountsController } from '@metamask/accounts-controller'; import { MultichainAssetsController, + MultiChainAssetsRatesController, MultichainBalancesController, } from '@metamask/assets-controllers'; +import { MultichainNetworkController } from '@metamask/multichain-network-controller'; import { MultichainTransactionsController } from '@metamask/multichain-transactions-controller'; import { CronjobController, @@ -42,8 +44,10 @@ export type Controller = | JsonSnapsRegistry | KeyringController | MultichainAssetsController + | MultiChainAssetsRatesController | MultichainBalancesController | MultichainTransactionsController + | MultichainNetworkController | NetworkController | OnboardingController | PermissionController< @@ -73,8 +77,10 @@ export type ControllerFlatState = AccountsController['state'] & JsonSnapsRegistry['state'] & KeyringController['state'] & MultichainAssetsController['state'] & + MultiChainAssetsRatesController['state'] & MultichainBalancesController['state'] & MultichainTransactionsController['state'] & + MultichainNetworkController['state'] & NetworkController['state'] & OnboardingController['state'] & PermissionController< diff --git a/app/scripts/controller-init/messengers/index.ts b/app/scripts/controller-init/messengers/index.ts index a00540f4aa15..43f20cd688b1 100644 --- a/app/scripts/controller-init/messengers/index.ts +++ b/app/scripts/controller-init/messengers/index.ts @@ -1,3 +1,4 @@ +import { noop } from 'lodash'; import { getPPOMControllerMessenger, getPPOMControllerInitMessenger, @@ -21,23 +22,37 @@ import { getMultichainBalancesControllerMessenger, getMultichainTransactionsControllerMessenger, getMultichainAssetsControllerMessenger, + getMultichainNetworkControllerMessenger, + getMultiChainAssetsRatesControllerMessenger, } from './multichain'; export const CONTROLLER_MESSENGERS = { CronjobController: { getMessenger: getCronjobControllerMessenger, + getInitMessenger: noop, }, ExecutionService: { getMessenger: getExecutionServiceMessenger, + getInitMessenger: noop, }, MultichainAssetsController: { getMessenger: getMultichainAssetsControllerMessenger, + getInitMessenger: noop, + }, + MultiChainAssetsRatesController: { + getMessenger: getMultiChainAssetsRatesControllerMessenger, + getInitMessenger: noop, }, MultichainBalancesController: { getMessenger: getMultichainBalancesControllerMessenger, + getInitMessenger: noop, }, MultichainTransactionsController: { getMessenger: getMultichainTransactionsControllerMessenger, + getInitMessenger: noop, + }, + MultichainNetworkController: { + getMessenger: getMultichainNetworkControllerMessenger, }, RateLimitController: { getMessenger: getRateLimitControllerMessenger, @@ -45,6 +60,7 @@ export const CONTROLLER_MESSENGERS = { }, SnapsRegistry: { getMessenger: getSnapsRegistryMessenger, + getInitMessenger: noop, }, SnapController: { getMessenger: getSnapControllerMessenger, @@ -52,9 +68,11 @@ export const CONTROLLER_MESSENGERS = { }, SnapInsightsController: { getMessenger: getSnapInsightsControllerMessenger, + getInitMessenger: noop, }, SnapInterfaceController: { getMessenger: getSnapInterfaceControllerMessenger, + getInitMessenger: noop, }, PPOMController: { getMessenger: getPPOMControllerMessenger, diff --git a/app/scripts/controller-init/messengers/multichain/index.ts b/app/scripts/controller-init/messengers/multichain/index.ts index 691c5e1e45ab..01aedc2e3627 100644 --- a/app/scripts/controller-init/messengers/multichain/index.ts +++ b/app/scripts/controller-init/messengers/multichain/index.ts @@ -1,7 +1,11 @@ export { getMultichainAssetsControllerMessenger } from './multichain-assets-controller-messenger'; +export { getMultiChainAssetsRatesControllerMessenger } from './multichain-assets-rates-controller-messenger'; export { getMultichainBalancesControllerMessenger } from './multichain-balances-controller-messenger'; export { getMultichainTransactionsControllerMessenger } from './multichain-transactions-controller-messenger'; +export { getMultichainNetworkControllerMessenger } from './multichain-network-controller-messenger'; export type { MultichainAssetsControllerMessenger } from './multichain-assets-controller-messenger'; +export type { MultiChainAssetsRatesControllerMessenger } from './multichain-assets-rates-controller-messenger'; export type { MultichainBalancesControllerMessenger } from './multichain-balances-controller-messenger'; export type { MultichainTransactionsControllerMessenger } from './multichain-transactions-controller-messenger'; +export type { MultichainNetworkControllerMessenger } from './multichain-network-controller-messenger'; diff --git a/app/scripts/controller-init/messengers/multichain/multichain-assets-rates-controller-messenger.test.ts b/app/scripts/controller-init/messengers/multichain/multichain-assets-rates-controller-messenger.test.ts new file mode 100644 index 000000000000..a74561b1c144 --- /dev/null +++ b/app/scripts/controller-init/messengers/multichain/multichain-assets-rates-controller-messenger.test.ts @@ -0,0 +1,14 @@ +import { Messenger, RestrictedMessenger } from '@metamask/base-controller'; +import { getMultiChainAssetsRatesControllerMessenger } from './multichain-assets-rates-controller-messenger'; + +describe('getMultiChainAssetsRatesControllerMessenger', () => { + it('returns a restricted messenger', () => { + const messenger = new Messenger(); + const multichainAssetsRatesControllerMessenger = + getMultiChainAssetsRatesControllerMessenger(messenger); + + expect(multichainAssetsRatesControllerMessenger).toBeInstanceOf( + RestrictedMessenger, + ); + }); +}); diff --git a/app/scripts/controller-init/messengers/multichain/multichain-assets-rates-controller-messenger.ts b/app/scripts/controller-init/messengers/multichain/multichain-assets-rates-controller-messenger.ts new file mode 100644 index 000000000000..94e5f0cc9c8c --- /dev/null +++ b/app/scripts/controller-init/messengers/multichain/multichain-assets-rates-controller-messenger.ts @@ -0,0 +1,61 @@ +import { Messenger } from '@metamask/base-controller'; +import { + AccountsControllerAccountAddedEvent, + AccountsControllerListMultichainAccountsAction, +} from '@metamask/accounts-controller'; +import { + CurrencyRateStateChange, + GetCurrencyRateState, + MultichainAssetsControllerStateChangeEvent, + MultichainAssetsControllerGetStateAction, +} from '@metamask/assets-controllers'; +import { + KeyringControllerLockEvent, + KeyringControllerUnlockEvent, +} from '@metamask/keyring-controller'; +import { HandleSnapRequest } from '@metamask/snaps-controllers'; + +type Actions = + | HandleSnapRequest + | AccountsControllerListMultichainAccountsAction + | GetCurrencyRateState + | MultichainAssetsControllerGetStateAction; + +type Events = + | KeyringControllerLockEvent + | KeyringControllerUnlockEvent + | AccountsControllerAccountAddedEvent + | CurrencyRateStateChange + | MultichainAssetsControllerStateChangeEvent; + +export type MultiChainAssetsRatesControllerMessenger = ReturnType< + typeof getMultiChainAssetsRatesControllerMessenger +>; + +/** + * Get a restricted messenger for the Multichain Assets Rate controller. This is scoped to the + * actions and events that the multichain Assets Rate controller is allowed to handle. + * + * @param messenger - The controller messenger to restrict. + * @returns The restricted controller messenger. + */ +export function getMultiChainAssetsRatesControllerMessenger( + messenger: Messenger, +) { + return messenger.getRestricted({ + name: 'MultiChainAssetsRatesController', + allowedEvents: [ + 'AccountsController:accountAdded', + 'KeyringController:lock', + 'KeyringController:unlock', + 'CurrencyRateController:stateChange', + 'MultichainAssetsController:stateChange', + ], + allowedActions: [ + 'AccountsController:listMultichainAccounts', + 'SnapController:handleRequest', + 'CurrencyRateController:getState', + 'MultichainAssetsController:getState', + ], + }); +} diff --git a/app/scripts/controller-init/messengers/multichain/multichain-network-controller-messenger.test.ts b/app/scripts/controller-init/messengers/multichain/multichain-network-controller-messenger.test.ts new file mode 100644 index 000000000000..82fa8f1738ec --- /dev/null +++ b/app/scripts/controller-init/messengers/multichain/multichain-network-controller-messenger.test.ts @@ -0,0 +1,14 @@ +import { Messenger, RestrictedMessenger } from '@metamask/base-controller'; +import { getMultichainNetworkControllerMessenger } from './multichain-network-controller-messenger'; + +describe('getMultichainNetworkControllerMessenger', () => { + it('returns a restricted messenger', () => { + const messenger = new Messenger(); + const multichainNetworkControllerMessenger = + getMultichainNetworkControllerMessenger(messenger); + + expect(multichainNetworkControllerMessenger).toBeInstanceOf( + RestrictedMessenger, + ); + }); +}); diff --git a/app/scripts/controller-init/messengers/multichain/multichain-network-controller-messenger.ts b/app/scripts/controller-init/messengers/multichain/multichain-network-controller-messenger.ts new file mode 100644 index 000000000000..f002cde6595b --- /dev/null +++ b/app/scripts/controller-init/messengers/multichain/multichain-network-controller-messenger.ts @@ -0,0 +1,36 @@ +import { Messenger } from '@metamask/base-controller'; +import { AccountsControllerSelectedAccountChangeEvent } from '@metamask/accounts-controller'; +import { + type NetworkControllerSetActiveNetworkAction, + type NetworkControllerGetStateAction, +} from '@metamask/network-controller'; + +type Actions = + | NetworkControllerSetActiveNetworkAction + | NetworkControllerGetStateAction; + +type Events = AccountsControllerSelectedAccountChangeEvent; + +export type MultichainNetworkControllerMessenger = ReturnType< + typeof getMultichainNetworkControllerMessenger +>; + +/** + * Get a restricted messenger for the Multichain Network controller. This is scoped to the + * actions and events that the Multichain Network controller is allowed to handle. + * + * @param messenger - The controller messenger to restrict. + * @returns The restricted controller messenger. + */ +export function getMultichainNetworkControllerMessenger( + messenger: Messenger, +) { + return messenger.getRestricted({ + name: 'MultichainNetworkController', + allowedActions: [ + 'NetworkController:setActiveNetwork', + 'NetworkController:getState', + ], + allowedEvents: ['AccountsController:selectedAccountChange'], + }); +} diff --git a/app/scripts/controller-init/multichain/index.ts b/app/scripts/controller-init/multichain/index.ts index 09a29be3f581..fc3c0a9d8d33 100644 --- a/app/scripts/controller-init/multichain/index.ts +++ b/app/scripts/controller-init/multichain/index.ts @@ -1,3 +1,5 @@ export { MultichainAssetsControllerInit } from './multichain-assets-controller-init'; export { MultichainBalancesControllerInit } from './multichain-balances-controller-init'; export { MultichainTransactionsControllerInit } from './multichain-transactions-controller-init'; +export { MultichainNetworkControllerInit } from './multichain-network-controller-init'; +export { MultiChainAssetsRatesControllerInit } from './multichain-rates-assets-controller-init'; diff --git a/app/scripts/controller-init/multichain/multichain-assets-controller-init.test.ts b/app/scripts/controller-init/multichain/multichain-assets-controller-init.test.ts index fa2f8e470e6a..0763242da197 100644 --- a/app/scripts/controller-init/multichain/multichain-assets-controller-init.test.ts +++ b/app/scripts/controller-init/multichain/multichain-assets-controller-init.test.ts @@ -20,6 +20,7 @@ function buildInitRequestMock(): jest.Mocked< controllerMessenger: getMultichainAssetsControllerMessenger( baseControllerMessenger, ), + initMessenger: undefined, }; } diff --git a/app/scripts/controller-init/multichain/multichain-balances-controller-init.test.ts b/app/scripts/controller-init/multichain/multichain-balances-controller-init.test.ts index 453aa81092a0..c51e9e1e454f 100644 --- a/app/scripts/controller-init/multichain/multichain-balances-controller-init.test.ts +++ b/app/scripts/controller-init/multichain/multichain-balances-controller-init.test.ts @@ -20,6 +20,7 @@ function buildInitRequestMock(): jest.Mocked< controllerMessenger: getMultichainBalancesControllerMessenger( baseControllerMessenger, ), + initMessenger: undefined, }; } diff --git a/app/scripts/controller-init/multichain/multichain-network-controller-init.test.ts b/app/scripts/controller-init/multichain/multichain-network-controller-init.test.ts new file mode 100644 index 000000000000..5a581c6d2db9 --- /dev/null +++ b/app/scripts/controller-init/multichain/multichain-network-controller-init.test.ts @@ -0,0 +1,52 @@ +import { MultichainNetworkController } from '@metamask/multichain-network-controller'; +import { Messenger } from '@metamask/base-controller'; +import { buildControllerInitRequestMock } from '../test/utils'; +import { ControllerInitRequest } from '../types'; +import { + MultichainNetworkControllerMessenger, + getMultichainNetworkControllerMessenger, +} from '../messengers/multichain'; +import { MultichainNetworkControllerInit } from './multichain-network-controller-init'; + +jest.mock('@metamask/multichain-network-controller'); + +const buildInitRequestMock = (): jest.Mocked< + ControllerInitRequest +> => { + const baseControllerMessenger = new Messenger(); + + return { + ...buildControllerInitRequestMock(), + controllerMessenger: getMultichainNetworkControllerMessenger( + baseControllerMessenger, + ), + initMessenger: undefined, + }; +}; + +describe('MultichainNetworkControllerInit', () => { + const multichainNetworkControllerClassMock = jest.mocked( + MultichainNetworkController, + ); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('returns controller instance', () => { + const requestMock = buildInitRequestMock(); + expect( + MultichainNetworkControllerInit(requestMock).controller, + ).toBeInstanceOf(MultichainNetworkController); + }); + + it('initializes with correct messenger and state', () => { + const requestMock = buildInitRequestMock(); + MultichainNetworkControllerInit(requestMock); + + expect(multichainNetworkControllerClassMock).toHaveBeenCalledWith({ + messenger: requestMock.controllerMessenger, + state: requestMock.persistedState.MultichainNetworkController, + }); + }); +}); diff --git a/app/scripts/controller-init/multichain/multichain-network-controller-init.ts b/app/scripts/controller-init/multichain/multichain-network-controller-init.ts new file mode 100644 index 000000000000..575df3283b6e --- /dev/null +++ b/app/scripts/controller-init/multichain/multichain-network-controller-init.ts @@ -0,0 +1,25 @@ +import { MultichainNetworkController } from '@metamask/multichain-network-controller'; +import { ControllerInitFunction } from '../types'; +import { MultichainNetworkControllerMessenger } from '../messengers/multichain'; + +/** + * Initialize the Multichain Network controller. + * + * @param request - The request object. + * @param request.controllerMessenger - The messenger to use for the controller. + * @param request.persistedState - The persisted state of the extension. + * @returns The initialized controller. + */ +export const MultichainNetworkControllerInit: ControllerInitFunction< + MultichainNetworkController, + MultichainNetworkControllerMessenger +> = ({ controllerMessenger, persistedState }) => { + const controller = new MultichainNetworkController({ + messenger: controllerMessenger, + state: persistedState.MultichainNetworkController, + }); + + return { + controller, + }; +}; diff --git a/app/scripts/controller-init/multichain/multichain-rates-assets-controller-init.test.ts b/app/scripts/controller-init/multichain/multichain-rates-assets-controller-init.test.ts new file mode 100644 index 000000000000..89244134a004 --- /dev/null +++ b/app/scripts/controller-init/multichain/multichain-rates-assets-controller-init.test.ts @@ -0,0 +1,52 @@ +import { MultiChainAssetsRatesController } from '@metamask/assets-controllers'; +import { Messenger } from '@metamask/base-controller'; +import { buildControllerInitRequestMock } from '../test/utils'; +import { ControllerInitRequest } from '../types'; +import { + getMultiChainAssetsRatesControllerMessenger, + MultiChainAssetsRatesControllerMessenger, +} from '../messengers/multichain'; +import { MultiChainAssetsRatesControllerInit } from './multichain-rates-assets-controller-init'; + +jest.mock('@metamask/assets-controllers'); + +function buildInitRequestMock(): jest.Mocked< + ControllerInitRequest +> { + const baseControllerMessenger = new Messenger(); + + return { + ...buildControllerInitRequestMock(), + controllerMessenger: getMultiChainAssetsRatesControllerMessenger( + baseControllerMessenger, + ), + initMessenger: undefined, + }; +} + +describe('MultiChainAssetsRatesControllerInit', () => { + const multiChainAssetsRatesControllerClassMock = jest.mocked( + MultiChainAssetsRatesController, + ); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('returns controller instance', () => { + const requestMock = buildInitRequestMock(); + expect( + MultiChainAssetsRatesControllerInit(requestMock).controller, + ).toBeInstanceOf(MultiChainAssetsRatesController); + }); + + it('initializes with correct messenger and state', () => { + const requestMock = buildInitRequestMock(); + MultiChainAssetsRatesControllerInit(requestMock); + + expect(multiChainAssetsRatesControllerClassMock).toHaveBeenCalledWith({ + messenger: requestMock.controllerMessenger, + state: requestMock.persistedState.MultiChainAssetsRatesController, + }); + }); +}); diff --git a/app/scripts/controller-init/multichain/multichain-rates-assets-controller-init.ts b/app/scripts/controller-init/multichain/multichain-rates-assets-controller-init.ts new file mode 100644 index 000000000000..120a23cf057b --- /dev/null +++ b/app/scripts/controller-init/multichain/multichain-rates-assets-controller-init.ts @@ -0,0 +1,25 @@ +import { MultiChainAssetsRatesController } from '@metamask/assets-controllers'; +import { ControllerInitFunction } from '../types'; +import { MultiChainAssetsRatesControllerMessenger } from '../messengers/multichain'; + +/** + * Initialize the Multichain Assets Rate controller. + * + * @param request - The request object. + * @param request.controllerMessenger - The messenger to use for the controller. + * @param request.persistedState - The persisted state of the extension. + * @returns The initialized controller. + */ +export const MultiChainAssetsRatesControllerInit: ControllerInitFunction< + MultiChainAssetsRatesController, + MultiChainAssetsRatesControllerMessenger +> = ({ controllerMessenger, persistedState }) => { + const controller = new MultiChainAssetsRatesController({ + messenger: controllerMessenger, + state: persistedState.MultiChainAssetsRatesController, + }); + + return { + controller, + }; +}; diff --git a/app/scripts/controller-init/multichain/multichain-transactions-controller-init.test.ts b/app/scripts/controller-init/multichain/multichain-transactions-controller-init.test.ts index ca41b5b177a5..f611b17e4ebf 100644 --- a/app/scripts/controller-init/multichain/multichain-transactions-controller-init.test.ts +++ b/app/scripts/controller-init/multichain/multichain-transactions-controller-init.test.ts @@ -20,6 +20,7 @@ function buildInitRequestMock(): jest.Mocked< controllerMessenger: getMultichainTransactionsControllerMessenger( baseControllerMessenger, ), + initMessenger: undefined, }; } diff --git a/app/scripts/controller-init/snaps/cronjob-controller-init.test.ts b/app/scripts/controller-init/snaps/cronjob-controller-init.test.ts index 932a956f8d8a..f31e28747af4 100644 --- a/app/scripts/controller-init/snaps/cronjob-controller-init.test.ts +++ b/app/scripts/controller-init/snaps/cronjob-controller-init.test.ts @@ -21,6 +21,7 @@ function getInitRequestMock(): jest.Mocked< const requestMock = { ...buildControllerInitRequestMock(), controllerMessenger: getCronjobControllerMessenger(baseMessenger), + initMessenger: undefined, }; return requestMock; diff --git a/app/scripts/controller-init/snaps/execution-service-init.test.ts b/app/scripts/controller-init/snaps/execution-service-init.test.ts index d5b877201e40..033baedd321a 100644 --- a/app/scripts/controller-init/snaps/execution-service-init.test.ts +++ b/app/scripts/controller-init/snaps/execution-service-init.test.ts @@ -24,6 +24,7 @@ function getInitRequestMock(): jest.Mocked< const requestMock = { ...buildControllerInitRequestMock(), controllerMessenger: getExecutionServiceMessenger(baseMessenger), + initMessenger: undefined, }; return requestMock; diff --git a/app/scripts/controller-init/snaps/snap-insights-controller-init.test.ts b/app/scripts/controller-init/snaps/snap-insights-controller-init.test.ts index bd83b4abc794..e0db4f98e0f5 100644 --- a/app/scripts/controller-init/snaps/snap-insights-controller-init.test.ts +++ b/app/scripts/controller-init/snaps/snap-insights-controller-init.test.ts @@ -18,6 +18,7 @@ function getInitRequestMock(): jest.Mocked< const requestMock = { ...buildControllerInitRequestMock(), controllerMessenger: getSnapInsightsControllerMessenger(baseMessenger), + initMessenger: undefined, }; return requestMock; diff --git a/app/scripts/controller-init/snaps/snap-interface-controller-init.test.ts b/app/scripts/controller-init/snaps/snap-interface-controller-init.test.ts index 2101ed1c706b..588c7eb7bac1 100644 --- a/app/scripts/controller-init/snaps/snap-interface-controller-init.test.ts +++ b/app/scripts/controller-init/snaps/snap-interface-controller-init.test.ts @@ -18,6 +18,7 @@ function getInitRequestMock(): jest.Mocked< const requestMock = { ...buildControllerInitRequestMock(), controllerMessenger: getSnapInterfaceControllerMessenger(baseMessenger), + initMessenger: undefined, }; return requestMock; diff --git a/app/scripts/controller-init/snaps/snaps-registry-init.test.ts b/app/scripts/controller-init/snaps/snaps-registry-init.test.ts index e26d51a7f588..46e33068442f 100644 --- a/app/scripts/controller-init/snaps/snaps-registry-init.test.ts +++ b/app/scripts/controller-init/snaps/snaps-registry-init.test.ts @@ -18,6 +18,7 @@ function getInitRequestMock(): jest.Mocked< const requestMock = { ...buildControllerInitRequestMock(), controllerMessenger: getSnapsRegistryMessenger(baseMessenger), + initMessenger: undefined, }; return requestMock; diff --git a/app/scripts/controller-init/types.ts b/app/scripts/controller-init/types.ts index 1f16af803bc4..9af50172feea 100644 --- a/app/scripts/controller-init/types.ts +++ b/app/scripts/controller-init/types.ts @@ -161,15 +161,13 @@ export type ControllerInitRequest< message: string, url?: string, ) => Promise; -} & (InitMessengerType extends BaseRestrictedControllerMessenger - ? { - /** - * Required initialization messenger instance. - * Generated using the callback specified in `getInitMessengerCallback`. - */ - initMessenger: InitMessengerType; - } - : unknown); + + /** + * Required initialization messenger instance. + * Generated using the callback specified in `getInitMessengerCallback`. + */ + initMessenger: InitMessengerType; +}; /** * A single background API method available to the UI. diff --git a/app/scripts/controller-init/utils.ts b/app/scripts/controller-init/utils.ts index 2bbaf7abf4ea..5a90aaecf229 100644 --- a/app/scripts/controller-init/utils.ts +++ b/app/scripts/controller-init/utils.ts @@ -29,14 +29,27 @@ export type InitControllersResult = { type BaseControllerInitRequest = ControllerInitRequest< BaseRestrictedControllerMessenger, - BaseRestrictedControllerMessenger + BaseRestrictedControllerMessenger | void >; type ControllerMessengerCallback = ( BaseControllerMessenger: BaseControllerMessenger, ) => BaseRestrictedControllerMessenger; -type ControllersToInitialize = 'PPOMController' | 'TransactionController'; +export type ControllersToInitialize = + | 'CronjobController' + | 'ExecutionService' + | 'MultichainAssetsController' + | 'MultiChainAssetsRatesController' + | 'MultichainBalancesController' + | 'MultichainTransactionsController' + | 'RateLimitController' + | 'SnapsRegistry' + | 'SnapController' + | 'SnapInsightsController' + | 'SnapInterfaceController' + | 'PPOMController' + | 'TransactionController'; type InitFunction = ControllerInitFunction< @@ -101,7 +114,6 @@ export function initControllers({ const messengerCallbacks = CONTROLLER_MESSENGERS[controllerName]; const controllerMessengerCallback = - // @ts-expect-error TODO: Resolve mismatch between base-controller versions. messengerCallbacks?.getMessenger as ControllerMessengerCallback; const initMessengerCallback = @@ -120,11 +132,8 @@ export function initControllers({ initMessenger, }; - // TODO: Remove @ts-expect-error once base-controller version mismatch is resolved - // Instead of suppressing all type errors, we'll be specific about the controllerMessenger mismatch const result = initFunction({ ...finalInitRequest, - // @ts-expect-error TODO: Resolve mismatch between base-controller versions. controllerMessenger: finalInitRequest.controllerMessenger, }); @@ -144,8 +153,8 @@ export function initControllers({ const memStateKey = memStateKeyRaw === null ? undefined : memStateKeyRaw ?? controllerName; - partialControllersByName[controllerName] = controller as Controller & - undefined; + // @ts-expect-error: Union too complex. + partialControllersByName[controllerName] = controller; controllerApi = { ...controllerApi, diff --git a/app/scripts/controllers/mmi-controller.test.ts b/app/scripts/controllers/mmi-controller.test.ts index 72e5bfd724f6..591603c0f2d5 100644 --- a/app/scripts/controllers/mmi-controller.test.ts +++ b/app/scripts/controllers/mmi-controller.test.ts @@ -117,6 +117,7 @@ describe('MMIController', function () { 'SnapKeyring:accountAssetListUpdated', 'SnapKeyring:accountBalancesUpdated', 'SnapKeyring:accountTransactionsUpdated', + 'MultichainNetworkController:networkDidChange', ], allowedActions: [ 'AccountsController:setCurrentAccount', @@ -773,6 +774,9 @@ describe('MMIController', function () { describe('handleMmiDashboardData', () => { it('should return internalAccounts as identities', async () => { + jest + .spyOn(mmiController.keyringController, 'getAccounts') + .mockReturnValue([mockAccount.address, mockAccount2.address]); const controllerMessengerSpy = jest.spyOn(mmiControllerMessenger, 'call'); await mmiController.handleMmiDashboardData(); diff --git a/app/scripts/controllers/permissions/enums.ts b/app/scripts/controllers/permissions/enums.ts index c170bd78aa67..03228d3594e2 100644 --- a/app/scripts/controllers/permissions/enums.ts +++ b/app/scripts/controllers/permissions/enums.ts @@ -1,5 +1,4 @@ export enum NOTIFICATION_NAMES { accountsChanged = 'metamask_accountsChanged', - unlockStateChanged = 'metamask_unlockStateChanged', chainChanged = 'metamask_chainChanged', } diff --git a/app/scripts/controllers/preferences-controller.test.ts b/app/scripts/controllers/preferences-controller.test.ts index efcd766ce1c9..f3703f3c94e7 100644 --- a/app/scripts/controllers/preferences-controller.test.ts +++ b/app/scripts/controllers/preferences-controller.test.ts @@ -4,6 +4,7 @@ import { Messenger } from '@metamask/base-controller'; import { AccountsController } from '@metamask/accounts-controller'; import { KeyringControllerStateChangeEvent } from '@metamask/keyring-controller'; +import type { MultichainNetworkControllerNetworkDidChangeEvent } from '@metamask/multichain-network-controller'; import { SnapControllerStateChangeEvent } from '@metamask/snaps-controllers'; import { Hex } from '@metamask/utils'; import { @@ -52,6 +53,7 @@ const setupController = ({ | SnapKeyringAccountAssetListUpdatedEvent | SnapKeyringAccountBalancesUpdatedEvent | SnapKeyringAccountTransactionsUpdatedEvent + | MultichainNetworkControllerNetworkDidChangeEvent >(); const preferencesControllerMessenger: PreferencesControllerMessenger = messenger.getRestricted({ @@ -85,6 +87,7 @@ const setupController = ({ 'SnapKeyring:accountAssetListUpdated', 'SnapKeyring:accountBalancesUpdated', 'SnapKeyring:accountTransactionsUpdated', + 'MultichainNetworkController:networkDidChange', ], allowedActions: [], }); diff --git a/app/scripts/lib/ppom/ppom-middleware.test.ts b/app/scripts/lib/ppom/ppom-middleware.test.ts index 16317fef0380..23acca61b639 100644 --- a/app/scripts/lib/ppom/ppom-middleware.test.ts +++ b/app/scripts/lib/ppom/ppom-middleware.test.ts @@ -12,7 +12,6 @@ import { createPPOMMiddleware, PPOMMiddlewareRequest } from './ppom-middleware'; import { generateSecurityAlertId, handlePPOMError, - isChainSupported, validateRequestWithPPOM, } from './ppom-util'; import { SecurityAlertResponse } from './types'; @@ -106,7 +105,6 @@ const createMiddleware = ( describe('PPOMMiddleware', () => { const generateSecurityAlertIdMock = jest.mocked(generateSecurityAlertId); const handlePPOMErrorMock = jest.mocked(handlePPOMError); - const isChainSupportedMock = jest.mocked(isChainSupported); const detectSIWEMock = jest.mocked(detectSIWE); beforeEach(() => { @@ -114,7 +112,6 @@ describe('PPOMMiddleware', () => { generateSecurityAlertIdMock.mockReturnValue(SECURITY_ALERT_ID_MOCK); handlePPOMErrorMock.mockReturnValue(SECURITY_ALERT_RESPONSE_MOCK); - isChainSupportedMock.mockResolvedValue(true); detectSIWEMock.mockReturnValue({ isSIWEMessage: false } as SIWEMessage); globalThis.sentry = { @@ -145,9 +142,7 @@ describe('PPOMMiddleware', () => { () => undefined, ); - expect(req.securityAlertResponse?.reason).toBe( - BlockaidReason.checkingChain, - ); + expect(req.securityAlertResponse?.reason).toBe(BlockaidReason.inProgress); expect(req.securityAlertResponse?.result_type).toBe( BlockaidResultType.Loading, ); diff --git a/app/scripts/lib/ppom/ppom-middleware.ts b/app/scripts/lib/ppom/ppom-middleware.ts index f99cf675f534..21013cc2dd1b 100644 --- a/app/scripts/lib/ppom/ppom-middleware.ts +++ b/app/scripts/lib/ppom/ppom-middleware.ts @@ -13,9 +13,9 @@ import { MESSAGE_TYPE } from '../../../../shared/constants/app'; import { SIGNING_METHODS } from '../../../../shared/constants/transaction'; import { PreferencesController } from '../../controllers/preferences-controller'; import { AppStateController } from '../../controllers/app-state-controller'; -import { SECURITY_ALERT_RESPONSE_CHECKING_CHAIN } from '../../../../shared/constants/security-provider'; import { getProviderConfig } from '../../../../shared/modules/selectors/networks'; import { trace, TraceContext, TraceName } from '../../../../shared/lib/trace'; +import { LOADING_SECURITY_ALERT_RESPONSE } from '../../../../shared/constants/security-provider'; import { generateSecurityAlertId, handlePPOMError, @@ -118,18 +118,18 @@ export function createPPOMMiddleware< }), ); - const securityAlertResponseCheckingChain: SecurityAlertResponse = { - ...SECURITY_ALERT_RESPONSE_CHECKING_CHAIN, + const securityAlertResponseLoading: SecurityAlertResponse = { + ...LOADING_SECURITY_ALERT_RESPONSE, securityAlertId, }; if (SIGNING_METHODS.includes(req.method)) { appStateController.addSignatureSecurityAlertResponse( - securityAlertResponseCheckingChain, + securityAlertResponseLoading, ); } - req.securityAlertResponse = securityAlertResponseCheckingChain; + req.securityAlertResponse = securityAlertResponseLoading; } catch (error) { req.securityAlertResponse = handlePPOMError( error, diff --git a/app/scripts/lib/ppom/ppom-util.test.ts b/app/scripts/lib/ppom/ppom-util.test.ts index c143f3dfe11b..0bb1deb72c86 100644 --- a/app/scripts/lib/ppom/ppom-util.test.ts +++ b/app/scripts/lib/ppom/ppom-util.test.ts @@ -15,13 +15,11 @@ import { BlockaidReason, BlockaidResultType, LOADING_SECURITY_ALERT_RESPONSE, - SECURITY_ALERT_RESPONSE_CHAIN_NOT_SUPPORTED, SecurityAlertSource, } from '../../../../shared/constants/security-provider'; import { AppStateController } from '../../controllers/app-state-controller'; import { generateSecurityAlertId, - isChainSupported, METHOD_SIGN_TYPED_DATA_V3, METHOD_SIGN_TYPED_DATA_V4, updateSecurityAlertResponse, @@ -114,10 +112,6 @@ describe('PPOM Utils', () => { const normalizeTransactionParamsMock = jest.mocked( normalizeTransactionParams, ); - const getSupportedChainIdsMock = jest.spyOn( - securityAlertAPI, - 'getSecurityAlertsAPISupportedChainIds', - ); let isSecurityAlertsEnabledMock: jest.SpyInstance; const updateSecurityAlertResponseMock = jest.fn(); @@ -308,23 +302,6 @@ describe('PPOM Utils', () => { }); }, ); - - it('updates response indicating chain is not supported', async () => { - const ppomController = {} as PPOMController; - const CHAIN_ID_UNSUPPORTED_MOCK = '0x2'; - - await validateRequestWithPPOM({ - ...validateRequestWithPPOMOptionsBase, - ppomController, - chainId: CHAIN_ID_UNSUPPORTED_MOCK, - }); - - expect(updateSecurityAlertResponseMock).toHaveBeenCalledWith( - validateRequestWithPPOMOptionsBase.request.method, - SECURITY_ALERT_ID_MOCK, - SECURITY_ALERT_RESPONSE_CHAIN_NOT_SUPPORTED, - ); - }); }); describe('generateSecurityAlertId', () => { @@ -457,36 +434,4 @@ describe('PPOM Utils', () => { ); }); }); - - describe('isChainSupported', () => { - describe('when security alerts API is enabled', () => { - beforeEach(async () => { - isSecurityAlertsEnabledMock.mockReturnValue(true); - getSupportedChainIdsMock.mockResolvedValue([CHAIN_ID_MOCK]); - }); - - it('returns true if chain is supported', async () => { - expect(await isChainSupported(CHAIN_ID_MOCK)).toStrictEqual(true); - }); - - it('returns false if chain is not supported', async () => { - expect(await isChainSupported('0x2')).toStrictEqual(false); - }); - - it('returns correctly if security alerts API throws', async () => { - getSupportedChainIdsMock.mockRejectedValue(new Error('Test Error')); - expect(await isChainSupported(CHAIN_ID_MOCK)).toStrictEqual(true); - }); - }); - - describe('when security alerts API is disabled', () => { - it('returns true if chain is supported', async () => { - expect(await isChainSupported(CHAIN_ID_MOCK)).toStrictEqual(true); - }); - - it('returns false if chain is not supported', async () => { - expect(await isChainSupported('0x2')).toStrictEqual(false); - }); - }); - }); }); diff --git a/app/scripts/lib/ppom/ppom-util.ts b/app/scripts/lib/ppom/ppom-util.ts index 847773baceee..e4b9f1373e63 100644 --- a/app/scripts/lib/ppom/ppom-util.ts +++ b/app/scripts/lib/ppom/ppom-util.ts @@ -12,15 +12,12 @@ import { BlockaidReason, BlockaidResultType, LOADING_SECURITY_ALERT_RESPONSE, - SECURITY_ALERT_RESPONSE_CHAIN_NOT_SUPPORTED, - SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS_FALLBACK_LIST, SecurityAlertSource, } from '../../../../shared/constants/security-provider'; import { SIGNING_METHODS } from '../../../../shared/constants/transaction'; import { AppStateController } from '../../controllers/app-state-controller'; import { SecurityAlertResponse, UpdateSecurityAlertResponse } from './types'; import { - getSecurityAlertsAPISupportedChainIds, isSecurityAlertsAPIEnabled, SecurityAlertsAPIRequest, validateWithSecurityAlertsAPI, @@ -56,15 +53,6 @@ export async function validateRequestWithPPOM({ updateSecurityAlertResponse: UpdateSecurityAlertResponse; }) { try { - if (!(await isChainSupported(chainId))) { - await updateSecurityResponse( - request.method, - securityAlertId, - SECURITY_ALERT_RESPONSE_CHAIN_NOT_SUPPORTED, - ); - return; - } - await updateSecurityResponse( request.method, securityAlertId, @@ -151,22 +139,6 @@ export function handlePPOMError( }; } -export async function isChainSupported(chainId: Hex): Promise { - let supportedChainIds = SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS_FALLBACK_LIST; - - try { - if (isSecurityAlertsAPIEnabled()) { - supportedChainIds = await getSecurityAlertsAPISupportedChainIds(); - } - } catch (error: unknown) { - handlePPOMError( - error, - `Error fetching supported chains from security alerts API`, - ); - } - return supportedChainIds.includes(chainId as Hex); -} - function normalizePPOMRequest( request: PPOMRequest | JsonRpcRequest, ): PPOMRequest | JsonRpcRequest { diff --git a/app/scripts/lib/ppom/security-alerts-api.test.ts b/app/scripts/lib/ppom/security-alerts-api.test.ts index 460139c1d359..6bb4b045b735 100644 --- a/app/scripts/lib/ppom/security-alerts-api.test.ts +++ b/app/scripts/lib/ppom/security-alerts-api.test.ts @@ -3,7 +3,6 @@ import { BlockaidResultType, } from '../../../../shared/constants/security-provider'; import { - getSecurityAlertsAPISupportedChainIds, isSecurityAlertsAPIEnabled, validateWithSecurityAlertsAPI, } from './security-alerts-api'; @@ -95,31 +94,4 @@ describe('Security Alerts API', () => { expect(isEnabled).toBe(false); }); }); - - describe('getSecurityAlertsAPISupportedChainIds', () => { - it('sends GET request', async () => { - const SUPPORTED_CHAIN_IDS_MOCK = ['0x1', '0x2']; - fetchMock.mockResolvedValue({ - ok: true, - json: async () => SUPPORTED_CHAIN_IDS_MOCK, - }); - const response = await getSecurityAlertsAPISupportedChainIds(); - - expect(response).toEqual(SUPPORTED_CHAIN_IDS_MOCK); - - expect(fetchMock).toHaveBeenCalledTimes(1); - expect(fetchMock).toHaveBeenCalledWith( - `${BASE_URL}/supportedChains`, - undefined, - ); - }); - - it('throws an error if response is not ok', async () => { - fetchMock.mockResolvedValue({ ok: false, status: 404 }); - - await expect(getSecurityAlertsAPISupportedChainIds()).rejects.toThrow( - 'Security alerts API request failed with status: 404', - ); - }); - }); }); diff --git a/app/scripts/lib/ppom/security-alerts-api.ts b/app/scripts/lib/ppom/security-alerts-api.ts index d0b6bc812b1a..9b9de32800e4 100644 --- a/app/scripts/lib/ppom/security-alerts-api.ts +++ b/app/scripts/lib/ppom/security-alerts-api.ts @@ -1,8 +1,7 @@ -import { Hex, JsonRpcRequest } from '@metamask/utils'; +import { JsonRpcRequest } from '@metamask/utils'; import { SecurityAlertResponse } from './types'; const ENDPOINT_VALIDATE = 'validate'; -const ENDPOINT_SUPPORTED_CHAINS = 'supportedChains'; type SecurityAlertsAPIRequestBody = { method: string; @@ -36,10 +35,6 @@ export async function validateWithSecurityAlertsAPI( }); } -export async function getSecurityAlertsAPISupportedChainIds(): Promise { - return request(ENDPOINT_SUPPORTED_CHAINS); -} - async function request(endpoint: string, options?: RequestInit) { const url = getUrl(endpoint); diff --git a/app/scripts/lib/snap-keyring/snap-keyring.test.ts b/app/scripts/lib/snap-keyring/snap-keyring.test.ts index 54388e412aa9..48c9a23a6cb6 100644 --- a/app/scripts/lib/snap-keyring/snap-keyring.test.ts +++ b/app/scripts/lib/snap-keyring/snap-keyring.test.ts @@ -1,6 +1,7 @@ import { Messenger } from '@metamask/base-controller'; import { EthAccountType, EthScope } from '@metamask/keyring-api'; import { InternalAccount } from '@metamask/keyring-internal-api'; +import { SnapId } from '@metamask/snaps-sdk'; import { SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES } from '../../../../shared/constants/app'; import { MetaMetricsEventCategory, @@ -15,6 +16,7 @@ import { SnapKeyringBuilderAllowActions, SnapKeyringBuilderMessenger, } from './types'; +import { getSnapName, isSnapPreinstalled } from './snaps'; const mockAddRequest = jest.fn(); const mockStartFlow = jest.fn(); @@ -22,14 +24,17 @@ const mockEndFlow = jest.fn(); const mockShowSuccess = jest.fn(); const mockShowError = jest.fn(); const mockGetAccounts = jest.fn(); -const mockSnapId = 'snapId'; +const mockSnapId = 'local:http://localhost:8080' as SnapId; const mockSnapName = 'mock-snap'; -const mockPersisKeyringHelper = jest.fn(); +const mockPersistKeyringHelper = jest.fn(); const mockSetSelectedAccount = jest.fn(); const mockSetAccountName = jest.fn(); const mockRemoveAccountHelper = jest.fn(); const mockTrackEvent = jest.fn(); const mockGetAccountByAddress = jest.fn(); +const mockLocale = 'en'; +const mockPreferencesControllerGetState = jest.fn(); +const mockSnapControllerGet = jest.fn(); const mockFlowId = '123'; const address = '0x2a4d4b667D5f12C3F9Bf8F14a7B9f8D8d9b8c8fA'; @@ -58,6 +63,12 @@ const mockInternalAccount = { }, }; +jest.mock('./snaps', () => ({ + ...jest.requireActual('./snaps'), + isSnapPreinstalled: jest.fn(), + getSnapName: jest.fn(), +})); + const createControllerMessenger = ({ account = mockInternalAccount, }: { @@ -80,6 +91,7 @@ const createControllerMessenger = ({ 'KeyringController:getAccounts', 'AccountsController:setSelectedAccount', 'AccountsController:getAccountByAddress', + 'PreferencesController:getState', ], allowedEvents: [], }); @@ -117,6 +129,19 @@ const createControllerMessenger = ({ case 'AccountsController:setAccountName': return mockSetAccountName.mockReturnValue(null)(params); + case 'PreferencesController:getState': + return mockPreferencesControllerGetState.mockReturnValue({ + locale: mockLocale, + })(params); + + case 'SnapController:get': + return mockSnapControllerGet.mockReturnValue({ + id: mockSnapId, + manifest: { + proposedName: mockSnapName, + }, + })(params); + default: throw new Error( `MOCK_FAIL - unsupported messenger call: ${actionType}`, @@ -129,19 +154,19 @@ const createControllerMessenger = ({ const createSnapKeyringBuilder = ({ snapName = mockSnapName, - isSnapPreinstalled = true, + snapPreinstalled = true, }: { snapName?: string; - isSnapPreinstalled?: boolean; + snapPreinstalled?: boolean; } = {}) => { - return snapKeyringBuilder( - createControllerMessenger(), - mockPersisKeyringHelper, - mockRemoveAccountHelper, - mockTrackEvent, - () => snapName, - () => isSnapPreinstalled, - ); + jest.mocked(isSnapPreinstalled).mockReturnValue(snapPreinstalled); + jest.mocked(getSnapName).mockReturnValue(snapName); + + return snapKeyringBuilder(createControllerMessenger(), { + persistKeyringHelper: mockPersistKeyringHelper, + removeAccountHelper: mockRemoveAccountHelper, + trackEvent: mockTrackEvent, + }); }; describe('Snap Keyring Methods', () => { @@ -225,7 +250,7 @@ describe('Snap Keyring Methods', () => { ]); // First call is from addAccount after user confirmation // Second call is from within the SnapKeyring after ending the addAccount flow - expect(mockPersisKeyringHelper).toHaveBeenCalledTimes(2); + expect(mockPersistKeyringHelper).toHaveBeenCalledTimes(2); expect(mockAddRequest).toHaveBeenNthCalledWith(2, [ { origin: mockSnapId, @@ -289,7 +314,7 @@ describe('Snap Keyring Methods', () => { expect(mockAddRequest).toHaveBeenCalledTimes(1); // First call is from addAccount after user confirmation // Second call is from within the SnapKeyring after ending the addAccount flow - expect(mockPersisKeyringHelper).toHaveBeenCalledTimes(2); + expect(mockPersistKeyringHelper).toHaveBeenCalledTimes(2); expect(mockAddRequest).toHaveBeenNthCalledWith(1, [ { origin: mockSnapId, @@ -349,7 +374,7 @@ describe('Snap Keyring Methods', () => { ]); // First call is from addAccount after user confirmation // Second call is from within the SnapKeyring - expect(mockPersisKeyringHelper).toHaveBeenCalledTimes(2); + expect(mockPersistKeyringHelper).toHaveBeenCalledTimes(2); expect(mockAddRequest).toHaveBeenNthCalledWith(2, [ { origin: mockSnapId, @@ -421,7 +446,7 @@ describe('Snap Keyring Methods', () => { expect(mockAddRequest).toHaveBeenCalledTimes(1); // First call is from addAccount after user confirmation // Second call is from within the SnapKeyring after ending the addAccount flow - expect(mockPersisKeyringHelper).toHaveBeenCalledTimes(2); + expect(mockPersistKeyringHelper).toHaveBeenCalledTimes(2); expect(mockAddRequest).toHaveBeenNthCalledWith(1, [ { origin: mockSnapId, @@ -484,7 +509,7 @@ describe('Snap Keyring Methods', () => { ]); // First call is from addAccount after user confirmation // Second call is from within the SnapKeyring - expect(mockPersisKeyringHelper).toHaveBeenCalledTimes(2); + expect(mockPersistKeyringHelper).toHaveBeenCalledTimes(2); expect(mockAddRequest).toHaveBeenNthCalledWith(2, [ { origin: mockSnapId, @@ -539,7 +564,7 @@ describe('Snap Keyring Methods', () => { it('ends approval flow on error', async () => { const errorMessage = 'save error'; - mockPersisKeyringHelper.mockRejectedValue(new Error(errorMessage)); + mockPersistKeyringHelper.mockRejectedValue(new Error(errorMessage)); const builder = createSnapKeyringBuilder(); await expect( builder().handleKeyringSnapMessage(mockSnapId, { diff --git a/app/scripts/lib/snap-keyring/snap-keyring.ts b/app/scripts/lib/snap-keyring/snap-keyring.ts index 8a6460cc99f0..48a39b728836 100644 --- a/app/scripts/lib/snap-keyring/snap-keyring.ts +++ b/app/scripts/lib/snap-keyring/snap-keyring.ts @@ -1,6 +1,7 @@ -import { SnapKeyring } from '@metamask/eth-snap-keyring'; +import { SnapKeyring, SnapKeyringCallbacks } from '@metamask/eth-snap-keyring'; import browser from 'webextension-polyfill'; import { SnapId } from '@metamask/snaps-sdk'; +import { assertIsValidSnapId } from '@metamask/snaps-utils'; import { MetaMetricsEventAccountType, MetaMetricsEventCategory, @@ -12,9 +13,28 @@ import MetamaskController from '../../metamask-controller'; // TODO: Remove restricted import // eslint-disable-next-line import/no-restricted-paths import { IconName } from '../../../../ui/components/component-library/icon'; +import MetaMetricsController from '../../controllers/metametrics-controller'; import { isBlockedUrl } from './utils/isBlockedUrl'; import { showError, showSuccess } from './utils/showResult'; import { SnapKeyringBuilderMessenger } from './types'; +import { getSnapName, isSnapPreinstalled } from './snaps'; + +/** + * Builder type for the Snap keyring. + */ +export type SnapKeyringBuilder = { + (): SnapKeyring; + type: typeof SnapKeyring.type; +}; + +/** + * Helpers for the Snap keyring implementation. + */ +export type SnapKeyringHelpers = { + trackEvent: MetaMetricsController['trackEvent']; + persistKeyringHelper: () => Promise; + removeAccountHelper: (address: string) => Promise; +}; /** * Get the addresses of the accounts managed by a given Snap. @@ -36,16 +56,16 @@ export const getAccountsBySnapId = async ( * This function will start the approval flow, show the account creation dialog, and end the flow. * * @param snapId - Snap ID to show the account creation dialog for. - * @param controllerMessenger - The controller messenger instance. + * @param messenger - The controller messenger instance. * @returns The user's confirmation result. */ export async function showAccountCreationDialog( snapId: string, - controllerMessenger: SnapKeyringBuilderMessenger, + messenger: SnapKeyringBuilderMessenger, ) { try { const confirmationResult = Boolean( - await controllerMessenger.call( + await messenger.call( 'ApprovalController:addRequest', { origin: snapId, @@ -66,17 +86,17 @@ export async function showAccountCreationDialog( * Show the account name suggestion confirmation dialog for a given Snap. * * @param snapId - Snap ID to show the account name suggestion dialog for. - * @param controllerMessenger - The controller messenger instance. + * @param messenger - The controller messenger instance. * @param accountNameSuggestion - Suggested name for the new account. * @returns The user's confirmation result. */ export async function showAccountNameSuggestionDialog( snapId: string, - controllerMessenger: SnapKeyringBuilderMessenger, + messenger: SnapKeyringBuilderMessenger, accountNameSuggestion: string, ): Promise<{ success: boolean; name?: string }> { try { - const confirmationResult = (await controllerMessenger.call( + const confirmationResult = (await messenger.call( 'ApprovalController:addRequest', { origin: snapId, @@ -93,348 +113,358 @@ export async function showAccountNameSuggestionDialog( } } -/** - * Constructs a SnapKeyring builder with specified handlers for managing snap accounts. - * - * @param controllerMessenger - The controller messenger instance. - * @param persistKeyringHelper - A function that persists all keyrings in the vault. - * @param removeAccountHelper - A function to help remove an account based on its address. - * @param trackEvent - A function to track MetaMetrics events. - * @param getSnapName - A function to get a snap's localized - * (or non-localized if there are no localization files) name from its manifest. - * @param isSnapPreinstalled - A function to check if a Snap is pre-installed. - * @returns The constructed SnapKeyring builder instance with the following methods: - * - `saveState`: Persists all keyrings in the keyring controller. - * - `addAccount`: Initiates the process of adding an account with user confirmation and handling the user input. - * - `removeAccount`: Initiates the process of removing an account with user confirmation and handling the user input. - */ -export const snapKeyringBuilder = ( - controllerMessenger: SnapKeyringBuilderMessenger, - persistKeyringHelper: () => Promise, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - removeAccountHelper: (address: string) => Promise, - trackEvent: ( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - payload: Record, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - options?: Record, - ) => void, - getSnapName: (snapId: string) => string, - isSnapPreinstalled: (snapId: string) => boolean, -) => { - const builder = (() => { - // @ts-expect-error TODO: Resolve mismatch between base-controller versions. - return new SnapKeyring(controllerMessenger, { - addressExists: async (address) => { - const addresses = await controllerMessenger.call( - 'KeyringController:getAccounts', - ); - return addresses.includes(address.toLowerCase()); - }, - redirectUser: async (snapId: string, url: string, message: string) => { - // Either url or message must be defined - if (url.length > 0 || message.length > 0) { - const isBlocked = await isBlockedUrl( - url, - async () => { - return await controllerMessenger.call( - 'PhishingController:maybeUpdateState', - ); - }, - (urlToTest: string) => { - return controllerMessenger.call( - 'PhishingController:testOrigin', - urlToTest, - ); - }, - ); +class SnapKeyringImpl implements SnapKeyringCallbacks { + readonly #messenger: SnapKeyringBuilderMessenger; - const confirmationResult = await controllerMessenger.call( - 'ApprovalController:addRequest', - { - origin: snapId, - requestData: { url, message, isBlockedUrl: isBlocked }, - type: SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES.showSnapAccountRedirect, - }, - true, - ); + readonly #trackEvent: SnapKeyringHelpers['trackEvent']; - if (Boolean(confirmationResult) && url.length > 0) { - browser.tabs.create({ url }); - } else { - console.log('User refused snap account redirection to:', url); - } - } else { - console.log( - 'Error occurred when redirecting snap account. url or message must be defined', + readonly #persistKeyringHelper: SnapKeyringHelpers['persistKeyringHelper']; + + readonly #removeAccountHelper: SnapKeyringHelpers['removeAccountHelper']; + + constructor( + messenger: SnapKeyringBuilderMessenger, + { + trackEvent, + persistKeyringHelper, + removeAccountHelper, + }: SnapKeyringHelpers, + ) { + this.#messenger = messenger; + this.#trackEvent = trackEvent; + this.#persistKeyringHelper = persistKeyringHelper; + this.#removeAccountHelper = removeAccountHelper; + } + + async addressExists(address: string) { + const addresses = await this.#messenger.call( + 'KeyringController:getAccounts', + ); + return addresses.includes(address.toLowerCase()); + } + + async redirectUser(snapId: string, url: string, message: string) { + // Either url or message must be defined + if (url.length > 0 || message.length > 0) { + const isBlocked = await isBlockedUrl( + url, + async () => { + return await this.#messenger.call( + 'PhishingController:maybeUpdateState', ); - } - }, - saveState: async () => { - await persistKeyringHelper(); - }, - addAccount: async ( - address: string, - snapId: string, - handleUserInput: (accepted: boolean) => Promise, - accountNameSuggestion: string = '', - displayConfirmation: boolean = false, - ) => { - const snapName = getSnapName(snapId); - const { id: addAccountFlowId } = controllerMessenger.call( - 'ApprovalController:startFlow', - ); + }, + (urlToTest: string) => { + return this.#messenger.call( + 'PhishingController:testOrigin', + urlToTest, + ); + }, + ); - const trackSnapAccountEvent = (event: MetaMetricsEventName) => { - trackEvent({ - event, - category: MetaMetricsEventCategory.Accounts, - properties: { - account_type: MetaMetricsEventAccountType.Snap, - snap_id: snapId, - snap_name: snapName, - }, - }); - }; + const confirmationResult = await this.#messenger.call( + 'ApprovalController:addRequest', + { + origin: snapId, + requestData: { url, message, isBlockedUrl: isBlocked }, + type: SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES.showSnapAccountRedirect, + }, + true, + ); + + if (Boolean(confirmationResult) && url.length > 0) { + browser.tabs.create({ url }); + } else { + console.log('User refused snap account redirection to:', url); + } + } else { + console.log( + 'Error occurred when redirecting snap account. url or message must be defined', + ); + } + } + + async saveState() { + await this.#persistKeyringHelper(); + } + + async addAccount( + address: string, + snapId: string, + handleUserInput: (accepted: boolean) => Promise, + accountNameSuggestion: string = '', + displayConfirmation: boolean = false, + ) { + assertIsValidSnapId(snapId); + const snapName = getSnapName(snapId, this.#messenger); + const { id: addAccountFlowId } = this.#messenger.call( + 'ApprovalController:startFlow', + ); + + const trackSnapAccountEvent = (event: MetaMetricsEventName) => { + this.#trackEvent({ + event, + category: MetaMetricsEventCategory.Accounts, + properties: { + account_type: MetaMetricsEventAccountType.Snap, + snap_id: snapId, + snap_name: snapName, + }, + }); + }; + + try { + const learnMoreLink = + 'https://support.metamask.io/managing-my-wallet/accounts-and-addresses/how-to-add-accounts-in-your-wallet/'; + + // If snap is preinstalled and does not request confirmation, skip the confirmation dialog + const skipConfirmation = + isSnapPreinstalled(snapId) && !displayConfirmation; + // If confirmation dialog are skipped, we consider the account creation to be confirmed until the account name dialog is closed + const accountCreationConfirmationResult = + skipConfirmation || + (await showAccountCreationDialog(snapId, this.#messenger)); + + if (!accountCreationConfirmationResult) { + // User has cancelled account creation + await handleUserInput(accountCreationConfirmationResult); + + throw new Error('User denied account creation'); + } + + const accountNameConfirmationResult = + await showAccountNameSuggestionDialog( + snapId, + this.#messenger, + accountNameSuggestion, + ); + + if (accountNameConfirmationResult?.success) { try { - const learnMoreLink = - 'https://support.metamask.io/managing-my-wallet/accounts-and-addresses/how-to-add-accounts-in-your-wallet/'; - - // If snap is preinstalled and does not request confirmation, skip the confirmation dialog - const skipConfirmation = - isSnapPreinstalled(snapId) && !displayConfirmation; - // If confirmation dialog are skipped, we consider the account creation to be confirmed until the account name dialog is closed - const accountCreationConfirmationResult = - skipConfirmation || - (await showAccountCreationDialog(snapId, controllerMessenger)); - - if (!accountCreationConfirmationResult) { - // User has cancelled account creation - await handleUserInput(accountCreationConfirmationResult); - - throw new Error('User denied account creation'); + // Persist the account so we can rename it afterward + await this.#persistKeyringHelper(); + await handleUserInput(accountNameConfirmationResult.success); + const account = this.#messenger.call( + 'AccountsController:getAccountByAddress', + address, + ); + if (!account) { + throw new Error( + `Internal account not found for address: ${address}`, + ); } + // Set the selected account to the new account + this.#messenger.call( + 'AccountsController:setSelectedAccount', + account.id, + ); - const accountNameConfirmationResult = - await showAccountNameSuggestionDialog( - snapId, - controllerMessenger, - accountNameSuggestion, + if (accountNameConfirmationResult.name) { + this.#messenger.call( + 'AccountsController:setAccountName', + account.id, + accountNameConfirmationResult.name, ); + } - if (accountNameConfirmationResult?.success) { - try { - // Persist the account so we can rename it afterward - await persistKeyringHelper(); - await handleUserInput(accountNameConfirmationResult.success); - const account = controllerMessenger.call( - 'AccountsController:getAccountByAddress', + if (!skipConfirmation) { + // TODO: Add events tracking to the dialog itself, so that events are more + // "linked" to UI actions + // User should now see the "Successfuly added account" page + trackSnapAccountEvent( + MetaMetricsEventName.AddSnapAccountSuccessViewed, + ); + await showSuccess( + this.#messenger, + snapId, + { + icon: IconName.UserCircleAdd, + title: t('snapAccountCreated'), + }, + { + message: t('snapAccountCreatedDescription') as string, address, - ); - if (!account) { - throw new Error( - `Internal account not found for address: ${address}`, - ); - } - // Set the selected account to the new account - controllerMessenger.call( - 'AccountsController:setSelectedAccount', - account.id, - ); - - if (accountNameConfirmationResult.name) { - controllerMessenger.call( - 'AccountsController:setAccountName', - account.id, - accountNameConfirmationResult.name, - ); - } - - if (!skipConfirmation) { - // TODO: Add events tracking to the dialog itself, so that events are more - // "linked" to UI actions - // User should now see the "Successfuly added account" page - trackSnapAccountEvent( - MetaMetricsEventName.AddSnapAccountSuccessViewed, - ); - await showSuccess( - controllerMessenger, - snapId, - { - icon: IconName.UserCircleAdd, - title: t('snapAccountCreated'), - }, - { - message: t('snapAccountCreatedDescription') as string, - address, - learnMoreLink, - }, - ); - // User has clicked on "OK" - trackSnapAccountEvent( - MetaMetricsEventName.AddSnapAccountSuccessClicked, - ); - } - - trackSnapAccountEvent(MetaMetricsEventName.AccountAdded); - } catch (e) { - // Error occurred while naming the account - const error = (e as Error).message; - - await showError( - controllerMessenger, - snapId, - { - icon: IconName.UserCircleAdd, - title: t('snapAccountCreationFailed'), - }, - { - message: t( - 'snapAccountCreationFailedDescription', - snapName, - ) as string, - learnMoreLink, - error, - }, - ); - - throw new Error( - `Error occurred while creating snap account: ${error}`, - ); - } - } else { - // User has cancelled account creation so remove the account from the keyring - await handleUserInput(accountNameConfirmationResult?.success); - - throw new Error('User denied account creation'); + learnMoreLink, + }, + ); + // User has clicked on "OK" + trackSnapAccountEvent( + MetaMetricsEventName.AddSnapAccountSuccessClicked, + ); } - } finally { - controllerMessenger.call('ApprovalController:endFlow', { - id: addAccountFlowId, - }); - } - }, - removeAccount: async ( - address: string, - snapId: string, - handleUserInput: (accepted: boolean) => Promise, - ) => { - const snapName = getSnapName(snapId); - const { id: removeAccountApprovalId } = controllerMessenger.call( - 'ApprovalController:startFlow', - ); - const learnMoreLink = - 'https://support.metamask.io/managing-my-wallet/accounts-and-addresses/how-to-remove-an-account-from-your-metamask-wallet/'; - - const trackSnapAccountEvent = (event: MetaMetricsEventName) => { - trackEvent({ - event, - category: MetaMetricsEventCategory.Accounts, - properties: { - account_type: MetaMetricsEventAccountType.Snap, - snap_id: snapId, - snap_name: snapName, + trackSnapAccountEvent(MetaMetricsEventName.AccountAdded); + } catch (e) { + // Error occurred while naming the account + const error = (e as Error).message; + + await showError( + this.#messenger, + snapId, + { + icon: IconName.UserCircleAdd, + title: t('snapAccountCreationFailed'), + }, + { + message: t( + 'snapAccountCreationFailedDescription', + snapName, + ) as string, + learnMoreLink, + error, }, - }); - }; + ); - // Since we use this in the finally, better to give it a default value if the controller call fails - let confirmationResult = false; + throw new Error( + `Error occurred while creating snap account: ${error}`, + ); + } + } else { + // User has cancelled account creation so remove the account from the keyring + await handleUserInput(accountNameConfirmationResult?.success); + + throw new Error('User denied account creation'); + } + } finally { + this.#messenger.call('ApprovalController:endFlow', { + id: addAccountFlowId, + }); + } + } + + async removeAccount( + address: string, + snapId: string, + handleUserInput: (accepted: boolean) => Promise, + ) { + assertIsValidSnapId(snapId); + + const snapName = getSnapName(snapId, this.#messenger); + const { id: removeAccountApprovalId } = this.#messenger.call( + 'ApprovalController:startFlow', + ); + + const learnMoreLink = + 'https://support.metamask.io/managing-my-wallet/accounts-and-addresses/how-to-remove-an-account-from-your-metamask-wallet/'; + + const trackSnapAccountEvent = (event: MetaMetricsEventName) => { + this.#trackEvent({ + event, + category: MetaMetricsEventCategory.Accounts, + properties: { + account_type: MetaMetricsEventAccountType.Snap, + snap_id: snapId, + snap_name: snapName, + }, + }); + }; + + // Since we use this in the finally, better to give it a default value if the controller call fails + let confirmationResult = false; + try { + confirmationResult = Boolean( + await this.#messenger.call( + 'ApprovalController:addRequest', + { + origin: snapId, + type: SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES.confirmAccountRemoval, + requestData: { publicAddress: address }, + }, + true, + ), + ); + + if (confirmationResult) { try { - confirmationResult = Boolean( - await controllerMessenger.call( - 'ApprovalController:addRequest', - { - origin: snapId, - type: SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES.confirmAccountRemoval, - requestData: { publicAddress: address }, - }, - true, - ), + await this.#removeAccountHelper(address); + await handleUserInput(confirmationResult); + await this.#persistKeyringHelper(); + + // TODO: Add events tracking to the dialog itself, so that events are more + // "linked" to UI actions + // User should now see the "Successfuly removed account" page + trackSnapAccountEvent( + MetaMetricsEventName.RemoveSnapAccountSuccessViewed, + ); + // This isn't actually an error, but we show it as one for styling reasons + await showError( + this.#messenger, + snapId, + { + icon: IconName.UserCircleRemove, + title: t('snapAccountRemoved'), + }, + { + message: t('snapAccountRemovedDescription') as string, + learnMoreLink, + }, ); - if (confirmationResult) { - try { - await removeAccountHelper(address); - await handleUserInput(confirmationResult); - await persistKeyringHelper(); - - // TODO: Add events tracking to the dialog itself, so that events are more - // "linked" to UI actions - // User should now see the "Successfuly removed account" page - trackSnapAccountEvent( - MetaMetricsEventName.RemoveSnapAccountSuccessViewed, - ); - // This isn't actually an error, but we show it as one for styling reasons - await showError( - controllerMessenger, - snapId, - { - icon: IconName.UserCircleRemove, - title: t('snapAccountRemoved'), - }, - { - message: t('snapAccountRemovedDescription') as string, - learnMoreLink, - }, - ); - - // User has clicked on "OK" - trackSnapAccountEvent( - MetaMetricsEventName.RemoveSnapAccountSuccessClicked, - ); - } catch (e) { - const error = (e as Error).message; - - await showError( - controllerMessenger, - snapId, - { - icon: IconName.UserCircleRemove, - title: t('snapAccountRemovalFailed'), - }, - { - message: t( - 'snapAccountRemovalFailedDescription', - snapName, - ) as string, - learnMoreLink, - error, - }, - ); - - trackSnapAccountEvent(MetaMetricsEventName.AccountRemoveFailed); - - throw new Error( - `Error occurred while removing snap account: ${error}`, - ); - } - } else { - await handleUserInput(confirmationResult); - - throw new Error('User denied account removal'); - } - } finally { - // We do not have a `else` clause here, as it's used if the request was - // canceled by the user, thus it's not a "fail" (not an error). - if (confirmationResult) { - trackSnapAccountEvent(MetaMetricsEventName.AccountRemoved); - } + // User has clicked on "OK" + trackSnapAccountEvent( + MetaMetricsEventName.RemoveSnapAccountSuccessClicked, + ); + } catch (e) { + const error = (e as Error).message; - controllerMessenger.call('ApprovalController:endFlow', { - id: removeAccountApprovalId, - }); + await showError( + this.#messenger, + snapId, + { + icon: IconName.UserCircleRemove, + title: t('snapAccountRemovalFailed'), + }, + { + message: t( + 'snapAccountRemovalFailedDescription', + snapName, + ) as string, + learnMoreLink, + error, + }, + ); + + trackSnapAccountEvent(MetaMetricsEventName.AccountRemoveFailed); + + throw new Error( + `Error occurred while removing snap account: ${error}`, + ); } - }, - }); - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - }) as any; + } else { + await handleUserInput(confirmationResult); + + throw new Error('User denied account removal'); + } + } finally { + // We do not have a `else` clause here, as it's used if the request was + // canceled by the user, thus it's not a "fail" (not an error). + if (confirmationResult) { + trackSnapAccountEvent(MetaMetricsEventName.AccountRemoved); + } + + this.#messenger.call('ApprovalController:endFlow', { + id: removeAccountApprovalId, + }); + } + } +} + +/** + * Constructs a SnapKeyring builder with specified handlers for managing Snap accounts. + * + * @param messenger - The messenger instace. + * @param helpers - Helpers required by the Snap keyring implementation. + * @returns A Snap keyring builder. + */ +export function snapKeyringBuilder( + messenger: SnapKeyringBuilderMessenger, + helpers: SnapKeyringHelpers, +) { + const builder = (() => { + // @ts-expect-error TODO: Resolve mismatch between base-controller versions. + return new SnapKeyring(messenger, new SnapKeyringImpl(messenger, helpers)); + }) as SnapKeyringBuilder; builder.type = SnapKeyring.type; + return builder; -}; +} diff --git a/app/scripts/lib/snap-keyring/snaps.ts b/app/scripts/lib/snap-keyring/snaps.ts new file mode 100644 index 000000000000..70c02a57ea3d --- /dev/null +++ b/app/scripts/lib/snap-keyring/snaps.ts @@ -0,0 +1,60 @@ +import { SnapId } from '@metamask/snaps-sdk'; +import { + getLocalizedSnapManifest, + isSnapId, + stripSnapPrefix, +} from '@metamask/snaps-utils'; +import PREINSTALLED_SNAPS from '../../snaps/preinstalled-snaps'; +import { SnapKeyringBuilderMessenger } from './types'; + +/** + * Assert that a Snap ID is valid. + * + * @param snapId - Snap ID to assert. + * @throws An error if the Snap ID is invalid. + */ +export function assetIsSnapId(snapId: string): asserts snapId is SnapId { + if (!isSnapId(snapId)) { + throw new Error(`Invalid Snap ID: ${snapId}`); + } +} + +/** + * Check if a Snap is a preinstalled Snap. + * + * @param snapId - Snap ID to verify. + * @returns True if Snap is a preinstalled Snap, false otherwise. + */ +export function isSnapPreinstalled(snapId: SnapId) { + return PREINSTALLED_SNAPS.some((snap) => snap.snapId === snapId); +} + +/** + * Get the localized Snap name or some fallback name otherwise. + * + * @param snapId - Snap ID. + * @param messenger - Snap keyring messenger. + * @returns The Snap name. + */ +export function getSnapName( + snapId: SnapId, + messenger: SnapKeyringBuilderMessenger, +) { + const { currentLocale } = messenger.call('PreferencesController:getState'); + const snap = messenger.call('SnapController:get', snapId); + + if (!snap) { + return stripSnapPrefix(snapId); + } + + if (snap.localizationFiles) { + const localizedManifest = getLocalizedSnapManifest( + snap.manifest, + currentLocale, + snap.localizationFiles, + ); + return localizedManifest.proposedName; + } + + return snap.manifest.proposedName; +} diff --git a/app/scripts/lib/snap-keyring/types.ts b/app/scripts/lib/snap-keyring/types.ts index 6fddafe4a840..db1e4e6c56f1 100644 --- a/app/scripts/lib/snap-keyring/types.ts +++ b/app/scripts/lib/snap-keyring/types.ts @@ -17,6 +17,7 @@ import type { StartFlow, } from '@metamask/approval-controller'; import { GetSnap, HandleSnapRequest } from '@metamask/snaps-controllers'; +import { PreferencesControllerGetStateAction } from '../../controllers/preferences-controller'; export type SnapKeyringBuilderAllowActions = | StartFlow @@ -34,7 +35,8 @@ export type SnapKeyringBuilderAllowActions = | AccountsControllerGetAccountByAddressAction | AccountsControllerSetAccountNameAction | HandleSnapRequest - | GetSnap; + | GetSnap + | PreferencesControllerGetStateAction; export type SnapKeyringBuilderMessenger = RestrictedMessenger< 'SnapKeyring', diff --git a/app/scripts/lib/transaction/util.test.ts b/app/scripts/lib/transaction/util.test.ts index 0a941968d802..f9b0a50fae1b 100644 --- a/app/scripts/lib/transaction/util.test.ts +++ b/app/scripts/lib/transaction/util.test.ts @@ -9,7 +9,6 @@ import { UserOperationController } from '@metamask/user-operation-controller'; import { cloneDeep } from 'lodash'; import { generateSecurityAlertId, - isChainSupported, validateRequestWithPPOM, } from '../ppom/ppom-util'; import { @@ -100,7 +99,6 @@ describe('Transaction Utils', () => { let userOperationController: jest.Mocked; const validateRequestWithPPOMMock = jest.mocked(validateRequestWithPPOM); const generateSecurityAlertIdMock = jest.mocked(generateSecurityAlertId); - const isChainSupportedMock = jest.mocked(isChainSupported); beforeEach(() => { jest.resetAllMocks(); @@ -125,7 +123,6 @@ describe('Transaction Utils', () => { }); generateSecurityAlertIdMock.mockReturnValue(SECURITY_ALERT_ID_MOCK); - isChainSupportedMock.mockResolvedValue(true); request.transactionController = transactionController; request.userOperationController = userOperationController; @@ -399,7 +396,7 @@ describe('Transaction Utils', () => { ).toHaveBeenCalledWith(TRANSACTION_PARAMS_MOCK, { ...TRANSACTION_OPTIONS_MOCK, securityAlertResponse: { - reason: BlockaidReason.checkingChain, + reason: BlockaidReason.inProgress, result_type: BlockaidResultType.Loading, securityAlertId: SECURITY_ALERT_ID_MOCK, }, diff --git a/app/scripts/lib/transaction/util.ts b/app/scripts/lib/transaction/util.ts index a3d0b929aff8..419def19be6e 100644 --- a/app/scripts/lib/transaction/util.ts +++ b/app/scripts/lib/transaction/util.ts @@ -24,7 +24,7 @@ import { UpdateSecurityAlertResponse, } from '../ppom/types'; import { - SECURITY_ALERT_RESPONSE_CHECKING_CHAIN, + LOADING_SECURITY_ALERT_RESPONSE, SECURITY_PROVIDER_EXCLUDED_TRANSACTION_TYPES, } from '../../../../shared/constants/security-provider'; import { endTrace, TraceName } from '../../../../shared/lib/trace'; @@ -287,13 +287,13 @@ async function validateSecurity(request: AddTransactionRequest) { updateSecurityAlertResponse, }); - const securityAlertResponseCheckingChain: SecurityAlertResponse = { - ...SECURITY_ALERT_RESPONSE_CHECKING_CHAIN, + const securityAlertResponseLoading: SecurityAlertResponse = { + ...LOADING_SECURITY_ALERT_RESPONSE, securityAlertId, }; request.transactionOptions.securityAlertResponse = - securityAlertResponseCheckingChain; + securityAlertResponseLoading; } catch (error) { handlePPOMError(error, 'Error validating JSON RPC using PPOM: '); } diff --git a/app/scripts/metamask-controller.actions.test.js b/app/scripts/metamask-controller.actions.test.js index f3dc22b46865..5ee364171ce2 100644 --- a/app/scripts/metamask-controller.actions.test.js +++ b/app/scripts/metamask-controller.actions.test.js @@ -202,7 +202,10 @@ describe('MetaMaskController', function () { describe('#setLocked', function () { it('should lock the wallet', async function () { + await metamaskController.createNewVaultAndKeychain('test@123'); + await metamaskController.setLocked(); + expect( metamaskController.keyringController.state.isUnlocked, ).toStrictEqual(false); diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index d4fc0ab09272..14724fbf1baa 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -129,13 +129,7 @@ import { TransactionType, } from '@metamask/transaction-controller'; -import { - ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) - getLocalizedSnapManifest, - stripSnapPrefix, - ///: END:ONLY_INCLUDE_IF - isSnapId, -} from '@metamask/snaps-utils'; +import { isSnapId } from '@metamask/snaps-utils'; import { Interface } from '@ethersproject/abi'; import { abiERC1155, abiERC721 } from '@metamask/metamask-eth-abis'; @@ -330,7 +324,6 @@ import { addDappTransaction, addTransaction } from './lib/transaction/util'; import { addTypedMessage, addPersonalMessage } from './lib/signature/util'; ///: END:ONLY_INCLUDE_IF import { LatticeKeyringOffscreen } from './lib/offscreen-bridge/lattice-offscreen-keyring'; -import PREINSTALLED_SNAPS from './snaps/preinstalled-snaps'; import { WeakRefObjectMap } from './lib/WeakRefObjectMap'; import { METAMASK_COOKIE_HANDLER } from './constants/stream'; @@ -358,13 +351,15 @@ import { handleBridgeTransactionFailed, handleTransactionFailedTypeBridge, } from './lib/bridge-status/metrics'; -///: BEGIN:ONLY_INCLUDE_IF(build-flask) import { + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) MultichainAssetsControllerInit, MultichainTransactionsControllerInit, MultichainBalancesControllerInit, + MultiChainAssetsRatesControllerInit, + ///: END:ONLY_INCLUDE_IF + MultichainNetworkControllerInit, } from './controller-init/multichain'; -///: END:ONLY_INCLUDE_IF import { TransactionControllerInit } from './controller-init/confirmations/transaction-controller-init'; import { PPOMControllerInit } from './controller-init/confirmations/ppom-controller-init'; import { initControllers } from './controller-init/utils'; @@ -632,6 +627,7 @@ export default class MetamaskController extends EventEmitter { 'SnapKeyring:accountAssetListUpdated', 'SnapKeyring:accountBalancesUpdated', 'SnapKeyring:accountTransactionsUpdated', + 'MultichainNetworkController:networkDidChange', ], allowedActions: [ 'KeyringController:getAccounts', @@ -1130,6 +1126,7 @@ export default class MetamaskController extends EventEmitter { 'AccountsController:setAccountName', 'SnapController:handleRequest', 'SnapController:get', + 'PreferencesController:getState', ], }); @@ -1139,44 +1136,12 @@ export default class MetamaskController extends EventEmitter { await this.accountsController.updateAccounts(); }; - const getSnapName = (id) => { - if (!id) { - return null; - } - - const currentLocale = this.getLocale(); - const { snaps } = this.snapController.state; - const snap = snaps[id]; - - if (!snap) { - return stripSnapPrefix(id); - } - - if (snap.localizationFiles) { - const localizedManifest = getLocalizedSnapManifest( - snap.manifest, - currentLocale, - snap.localizationFiles, - ); - return localizedManifest.proposedName; - } - - return snap.manifest.proposedName; - }; - - const isSnapPreinstalled = (id) => { - return PREINSTALLED_SNAPS.some((snap) => snap.snapId === id); - }; - additionalKeyrings.push( - snapKeyringBuilder( - snapKeyringBuildMessenger, - persistAndUpdateAccounts, - (address) => this.removeAccount(address), - this.metaMetricsController.trackEvent.bind(this.metaMetricsController), - getSnapName, - isSnapPreinstalled, - ), + snapKeyringBuilder(snapKeyringBuildMessenger, { + persistKeyringHelper: () => persistAndUpdateAccounts(), + removeAccountHelper: (address) => this.removeAccount(address), + trackEvent: (...args) => this.metaMetricsController.trackEvent(...args), + }), ); ///: END:ONLY_INCLUDE_IF @@ -2054,9 +2019,11 @@ export default class MetamaskController extends EventEmitter { TransactionController: TransactionControllerInit, ///: BEGIN:ONLY_INCLUDE_IF(build-flask) MultichainAssetsController: MultichainAssetsControllerInit, + MultiChainAssetsRatesController: MultiChainAssetsRatesControllerInit, MultichainBalancesController: MultichainBalancesControllerInit, MultichainTransactionsController: MultichainTransactionsControllerInit, ///: END:ONLY_INCLUDE_IF + MultichainNetworkController: MultichainNetworkControllerInit, }; const { @@ -2091,7 +2058,11 @@ export default class MetamaskController extends EventEmitter { controllersByName.MultichainBalancesController; this.multichainTransactionsController = controllersByName.MultichainTransactionsController; + this.multiChainAssetsRatesController = + controllersByName.MultiChainAssetsRatesController; ///: END:ONLY_INCLUDE_IF + this.multichainNetworkController = + controllersByName.MultichainNetworkController; this.controllerMessenger.subscribe( 'TransactionController:transactionStatusUpdated', @@ -2219,6 +2190,7 @@ export default class MetamaskController extends EventEmitter { MetaMetricsDataDeletionController: this.metaMetricsDataDeletionController, AddressBookController: this.addressBookController, CurrencyController: this.currencyRateController, + MultichainNetworkController: this.multichainNetworkController, NetworkController: this.networkController, AlertController: this.alertController, OnboardingController: this.onboardingController, @@ -2266,7 +2238,9 @@ export default class MetamaskController extends EventEmitter { MultichainAssetsController: this.multichainAssetsController, MultichainBalancesController: this.multichainBalancesController, MultichainTransactionsController: this.multichainTransactionsController, + MultiChainAssetsRatesController: this.multiChainAssetsRatesController, ///: END:ONLY_INCLUDE_IF + MultichainNetworkController: this.multichainNetworkController, NetworkController: this.networkController, KeyringController: this.keyringController, PreferencesController: this.preferencesController, @@ -2775,6 +2749,10 @@ export default class MetamaskController extends EventEmitter { if (chains.length > 0 && !chains.includes(currentChainIdForOrigin)) { const networkClientId = this.networkController.findNetworkClientIdByChainId(chains[0]); + // setActiveNetwork should be called before setNetworkClientIdForDomain + // to ensure that the isConnected value can be accurately inferred from + // NetworkController.state.networksMetadata in return value of + // `metamask_getProviderState` requests and `metamask_chainChanged` events. this.networkController.setActiveNetwork(networkClientId); this.selectedNetworkController.setNetworkClientIdForDomain( origin, @@ -3026,7 +3004,11 @@ export default class MetamaskController extends EventEmitter { const providerNetworkState = await this.getProviderNetworkState(origin); return { - isUnlocked: this.isUnlocked(), + /** + * We default `isUnlocked` to `true` because even though we no longer emit events depending on this, + * embedded dapp providers might listen directly to our streams, and therefore depend on it, so we leave it here. + */ + isUnlocked: true, accounts: this.getPermittedAccounts(origin), ...providerNetworkState, }; @@ -4069,6 +4051,12 @@ export default class MetamaskController extends EventEmitter { // MultichainBalancesController multichainUpdateBalance: (accountId) => this.multichainBalancesController.updateBalance(accountId), + + // MultichainTransactionsController + multichainUpdateTransactions: (accountId) => + this.multichainTransactionsController.updateTransactionsForAccount( + accountId, + ), ///: END:ONLY_INCLUDE_IF // Transaction Decode decodeTransactionData: (request) => @@ -6522,20 +6510,8 @@ export default class MetamaskController extends EventEmitter { /** * Handle global application unlock. - * Notifies all connections that the extension is unlocked, and which - * account(s) are currently accessible, if any. */ _onUnlock() { - this.notifyAllConnections((origin) => { - return { - method: NOTIFICATION_NAMES.unlockStateChanged, - params: { - isUnlocked: true, - accounts: this.getPermittedAccounts(origin), - }, - }; - }); - this.unMarkPasswordForgotten(); // In the current implementation, this handler is triggered by a @@ -6546,16 +6522,8 @@ export default class MetamaskController extends EventEmitter { /** * Handle global application lock. - * Notifies all connections that the extension is locked. */ _onLock() { - this.notifyAllConnections({ - method: NOTIFICATION_NAMES.unlockStateChanged, - params: { - isUnlocked: false, - }, - }); - // In the current implementation, this handler is triggered by a // KeyringController event. Other controllers subscribe to the 'lock' // event of the MetaMaskController itself. @@ -7084,21 +7052,19 @@ export default class MetamaskController extends EventEmitter { } _notifyAccountsChange(origin, newAccounts) { - if (this.isUnlocked()) { - this.notifyConnections(origin, { - method: NOTIFICATION_NAMES.accountsChanged, - // This should be the same as the return value of `eth_accounts`, - // namely an array of the current / most recently selected Ethereum - // account. - params: - newAccounts.length < 2 - ? // If the length is 1 or 0, the accounts are sorted by definition. - newAccounts - : // If the length is 2 or greater, we have to execute - // `eth_accounts` vi this method. - this.getPermittedAccounts(origin), - }); - } + this.notifyConnections(origin, { + method: NOTIFICATION_NAMES.accountsChanged, + // This should be the same as the return value of `eth_accounts`, + // namely an array of the current / most recently selected Ethereum + // account. + params: + newAccounts.length < 2 + ? // If the length is 1 or 0, the accounts are sorted by definition. + newAccounts + : // If the length is 2 or greater, we have to execute + // `eth_accounts` vi this method. + this.getPermittedAccounts(origin), + }); this.permissionLogController.updateAccountsHistory(origin, newAccounts); } diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 932acec5a519..ab0f4cb64f5c 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -638,6 +638,7 @@ describe('MetaMaskController', () => { describe('setLocked', () => { it('should lock KeyringController', async () => { + await metamaskController.createNewVaultAndKeychain('password'); jest.spyOn(metamaskController.keyringController, 'setLocked'); await metamaskController.setLocked(); @@ -2650,17 +2651,18 @@ describe('MetaMaskController', () => { }); describe('#addNewAccount', () => { - it('errors when an primary keyring is does not exist', async () => { + it('throws an error if the keyring controller is locked', async () => { const addNewAccount = metamaskController.addNewAccount(); - - await expect(addNewAccount).rejects.toThrow('No HD keyring found'); + await expect(addNewAccount).rejects.toThrow( + 'KeyringController - The operation cannot be completed while the controller is locked.', + ); }); }); describe('#getSeedPhrase', () => { - it('errors when no password is provided', async () => { + it('throws error if keyring controller is locked', async () => { await expect(metamaskController.getSeedPhrase()).rejects.toThrow( - 'KeyringController - Cannot unlock without a previous vault.', + 'KeyringController - The operation cannot be completed while the controller is locked.', ); }); diff --git a/app/scripts/migrations/144.test.ts b/app/scripts/migrations/144.test.ts new file mode 100644 index 000000000000..55bd0e412803 --- /dev/null +++ b/app/scripts/migrations/144.test.ts @@ -0,0 +1,87 @@ +import { migrate, version } from './144'; + +const oldVersion = 143; + +const DEFAULT_CURRENCY = 'usd'; +const VALID_CURRENCY = 'eur'; +const INVALID_CURRENCY = 'INVALID_CURRENCY'; + +describe(`migration #${version}`, () => { + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.meta).toStrictEqual({ version }); + }); + + describe(`migration #${version}`, () => { + it('does nothing if CurrencyController is missing', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({}); + }); + + it('does nothing if CurrencyController is not an object', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + CurrencyController: 'invalidData', + }, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('sets currentCurrency to "USD" if it is missing', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + CurrencyController: {}, + }, + }; + const expectedData = { + CurrencyController: { + currentCurrency: DEFAULT_CURRENCY, + }, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(expectedData); + }); + + it('sets currentCurrency to "USD" if it is invalid', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + CurrencyController: { + currentCurrency: INVALID_CURRENCY, + }, + }, + }; + const expectedData = { + CurrencyController: { + currentCurrency: DEFAULT_CURRENCY, + }, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(expectedData); + }); + + it('does nothing if currentCurrency is valid', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + CurrencyController: { + currentCurrency: VALID_CURRENCY, + }, + }, + }; + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + }); +}); diff --git a/app/scripts/migrations/144.ts b/app/scripts/migrations/144.ts new file mode 100644 index 000000000000..77a526605439 --- /dev/null +++ b/app/scripts/migrations/144.ts @@ -0,0 +1,71 @@ +import { hasProperty } from '@metamask/utils'; +import { cloneDeep, isObject } from 'lodash'; +import { PRICE_API_CURRENCIES } from '../../../shared/constants/price-api-currencies'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +type CurrencyController = { + currentCurrency?: string; +}; + +export const version = 144; +const DEFAULT_CURRENCY = 'usd'; + +/** + * This migration ensures that the `currentCurrency` in `CurrencyController` + * is set to a valid available currency. If it's missing or invalid, it defaults to "USD". + * + * @param originalVersionedData - The original MetaMask extension state. + * @returns Updated versioned MetaMask extension state. + */ +export async function migrate( + originalVersionedData: VersionedData, +): Promise { + const versionedData = cloneDeep(originalVersionedData); + versionedData.meta.version = version; + transformState(versionedData.data); + return versionedData; +} + +function transformState(state: Record) { + if (!hasProperty(state, 'CurrencyController')) { + global.sentry?.captureException?.( + new Error(`Migration ${version}: Missing CurrencyController in state`), + ); + return; + } + + const currencyController = state.CurrencyController as CurrencyController; + + if (!isObject(currencyController)) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: Invalid CurrencyController state type '${typeof currencyController}'`, + ), + ); + return; + } + + const { currentCurrency } = currencyController; + + if (!currentCurrency) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: Missing currentCurrency in CurrencyController, defaulting to ${DEFAULT_CURRENCY}`, + ), + ); + currencyController.currentCurrency = DEFAULT_CURRENCY; + return; + } + + const isValidCurrency = PRICE_API_CURRENCIES.some( + (currency) => currency === currentCurrency, + ); + + if (!isValidCurrency) { + currencyController.currentCurrency = DEFAULT_CURRENCY; + } +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 6df962d4709f..1306d1bca3aa 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -168,6 +168,7 @@ const migrations = [ require('./141'), require('./142'), require('./143'), + require('./144'), ]; export default migrations; diff --git a/development/circular-deps.jsonc b/development/circular-deps.jsonc index dcd08ec5b46d..1fa13db041a0 100644 --- a/development/circular-deps.jsonc +++ b/development/circular-deps.jsonc @@ -48,10 +48,6 @@ "ui/components/multichain/pages/send/components/index.ts", "ui/components/multichain/pages/send/components/your-accounts.tsx" ], - [ - "ui/pages/asset/components/asset-page.tsx", - "ui/pages/asset/components/token-buttons.tsx" - ], [ "ui/pages/notifications/notifications-list.tsx", "ui/pages/notifications/notifications.tsx" diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 291c431e0117..9a0873cb879c 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -412,7 +412,7 @@ "@ethersproject/bytes": true, "ethers>@ethersproject/logger": true, "ethers>@ethersproject/properties": true, - "ethers>@ethersproject/signing-key>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethers>@ethersproject/solidity": { @@ -1434,6 +1434,14 @@ "@metamask/keyring-api>bech32": true } }, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>@metamask/keyring-api": { + "packages": { + "@metamask/keyring-api>@metamask/keyring-utils": true, + "@metamask/utils>@metamask/superstruct": true, + "@metamask/multichain-transactions-controller>@metamask/utils": true, + "@metamask/keyring-api>bech32": true + } + }, "@metamask/keyring-controller": { "packages": { "@ethereumjs/tx>@ethereumjs/util": true, @@ -1460,6 +1468,14 @@ "@metamask/keyring-snap-client>uuid": true } }, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client": { + "packages": { + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>@metamask/keyring-api": true, + "@metamask/keyring-api>@metamask/keyring-utils": true, + "@metamask/utils>@metamask/superstruct": true, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>uuid": true + } + }, "@metamask/keyring-api>@metamask/keyring-utils": { "globals": { "URL": true @@ -1518,6 +1534,27 @@ "lodash": true } }, + "@metamask/multichain-network-controller": { + "packages": { + "@metamask/base-controller": true, + "@metamask/keyring-api": true, + "@metamask/network-controller": true, + "@metamask/multichain-network-controller>@metamask/utils": true, + "@metamask/multichain-network-controller>@solana/addresses": true + } + }, + "@metamask/multichain-transactions-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/base-controller": true, + "@metamask/keyring-api": true, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client": true, + "@metamask/snaps-utils": true, + "@metamask/multichain-transactions-controller>@metamask/utils": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2397,6 +2434,36 @@ "semver": true } }, + "@metamask/multichain-network-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@noble/hashes": true, + "@metamask/utils>@scure/base": true, + "browserify>buffer": true, + "nock>debug": true, + "@metamask/utils>pony-cause": true, + "semver": true + } + }, + "@metamask/multichain-transactions-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@noble/hashes": true, + "@metamask/utils>@scure/base": true, + "browserify>buffer": true, + "nock>debug": true, + "@metamask/utils>pony-cause": true, + "semver": true + } + }, "@metamask/name-controller>@metamask/utils": { "globals": { "TextDecoder": true, @@ -2982,6 +3049,20 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses": { + "globals": { + "Intl.Collator": true, + "TextEncoder": true, + "crypto.subtle.digest": true, + "crypto.subtle.exportKey": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/assertions": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-strings": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/assertions": { "globals": { "crypto": true, @@ -2991,11 +3072,25 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/assertions": { + "globals": { + "crypto": true, + "isSecureContext": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/codecs-core": { "packages": { "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": { + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/codecs-strings": { "globals": { "TextDecoder": true, @@ -3008,11 +3103,28 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-strings": { + "globals": { + "TextDecoder": true, + "TextEncoder": true, + "atob": true, + "btoa": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/errors": { "globals": { "btoa": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": { + "globals": { + "btoa": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -3916,17 +4028,6 @@ "stream-browserify": true } }, - "ethers>@ethersproject/signing-key>elliptic": { - "packages": { - "bn.js": true, - "@metamask/ppom-validator>elliptic>brorand": true, - "ethers>@ethersproject/sha2>hash.js": true, - "@metamask/ppom-validator>elliptic>hmac-drbg": true, - "pumpify>inherits": true, - "@metamask/ppom-validator>elliptic>minimalistic-assert": true, - "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true - } - }, "@metamask/ppom-validator>elliptic": { "packages": { "bn.js": true, @@ -3938,17 +4039,6 @@ "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true } }, - "eth-lattice-keyring>gridplus-sdk>elliptic": { - "packages": { - "bn.js": true, - "@metamask/ppom-validator>elliptic>brorand": true, - "ethers>@ethersproject/sha2>hash.js": true, - "@metamask/ppom-validator>elliptic>hmac-drbg": true, - "pumpify>inherits": true, - "@metamask/ppom-validator>elliptic>minimalistic-assert": true, - "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true - } - }, "@metamask/eth-token-tracker>deep-equal>es-get-iterator": { "packages": { "string.prototype.matchall>call-bind": true, @@ -4266,7 +4356,7 @@ "ethereumjs-util>ethereum-cryptography>bs58check": true, "browserify>buffer": true, "@ethereumjs/tx>@ethereumjs/common>crc-32": true, - "eth-lattice-keyring>gridplus-sdk>elliptic": true, + "@metamask/ppom-validator>elliptic": true, "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": true, "ethers>@ethersproject/sha2>hash.js": true, "eth-ens-namehash>js-sha3": true, @@ -5896,6 +5986,11 @@ "crypto": true } }, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>uuid": { + "globals": { + "crypto": true + } + }, "eth-lattice-keyring>gridplus-sdk>uuid": { "globals": { "crypto": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index bddf699c5cd9..9a0873cb879c 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -412,7 +412,7 @@ "@ethersproject/bytes": true, "ethers>@ethersproject/logger": true, "ethers>@ethersproject/properties": true, - "ethers>@ethersproject/signing-key>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethers>@ethersproject/solidity": { @@ -1534,6 +1534,15 @@ "lodash": true } }, + "@metamask/multichain-network-controller": { + "packages": { + "@metamask/base-controller": true, + "@metamask/keyring-api": true, + "@metamask/network-controller": true, + "@metamask/multichain-network-controller>@metamask/utils": true, + "@metamask/multichain-network-controller>@solana/addresses": true + } + }, "@metamask/multichain-transactions-controller": { "globals": { "console.error": true @@ -2425,6 +2434,21 @@ "semver": true } }, + "@metamask/multichain-network-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@noble/hashes": true, + "@metamask/utils>@scure/base": true, + "browserify>buffer": true, + "nock>debug": true, + "@metamask/utils>pony-cause": true, + "semver": true + } + }, "@metamask/multichain-transactions-controller>@metamask/utils": { "globals": { "TextDecoder": true, @@ -3025,6 +3049,20 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses": { + "globals": { + "Intl.Collator": true, + "TextEncoder": true, + "crypto.subtle.digest": true, + "crypto.subtle.exportKey": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/assertions": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-strings": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/assertions": { "globals": { "crypto": true, @@ -3034,11 +3072,25 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/assertions": { + "globals": { + "crypto": true, + "isSecureContext": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/codecs-core": { "packages": { "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": { + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/codecs-strings": { "globals": { "TextDecoder": true, @@ -3051,11 +3103,28 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-strings": { + "globals": { + "TextDecoder": true, + "TextEncoder": true, + "atob": true, + "btoa": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/errors": { "globals": { "btoa": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": { + "globals": { + "btoa": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -3959,17 +4028,6 @@ "stream-browserify": true } }, - "ethers>@ethersproject/signing-key>elliptic": { - "packages": { - "bn.js": true, - "@metamask/ppom-validator>elliptic>brorand": true, - "ethers>@ethersproject/sha2>hash.js": true, - "@metamask/ppom-validator>elliptic>hmac-drbg": true, - "pumpify>inherits": true, - "@metamask/ppom-validator>elliptic>minimalistic-assert": true, - "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true - } - }, "@metamask/ppom-validator>elliptic": { "packages": { "bn.js": true, @@ -3981,17 +4039,6 @@ "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true } }, - "eth-lattice-keyring>gridplus-sdk>elliptic": { - "packages": { - "bn.js": true, - "@metamask/ppom-validator>elliptic>brorand": true, - "ethers>@ethersproject/sha2>hash.js": true, - "@metamask/ppom-validator>elliptic>hmac-drbg": true, - "pumpify>inherits": true, - "@metamask/ppom-validator>elliptic>minimalistic-assert": true, - "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true - } - }, "@metamask/eth-token-tracker>deep-equal>es-get-iterator": { "packages": { "string.prototype.matchall>call-bind": true, @@ -4309,7 +4356,7 @@ "ethereumjs-util>ethereum-cryptography>bs58check": true, "browserify>buffer": true, "@ethereumjs/tx>@ethereumjs/common>crc-32": true, - "eth-lattice-keyring>gridplus-sdk>elliptic": true, + "@metamask/ppom-validator>elliptic": true, "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": true, "ethers>@ethersproject/sha2>hash.js": true, "eth-ens-namehash>js-sha3": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 291c431e0117..9a0873cb879c 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -412,7 +412,7 @@ "@ethersproject/bytes": true, "ethers>@ethersproject/logger": true, "ethers>@ethersproject/properties": true, - "ethers>@ethersproject/signing-key>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethers>@ethersproject/solidity": { @@ -1434,6 +1434,14 @@ "@metamask/keyring-api>bech32": true } }, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>@metamask/keyring-api": { + "packages": { + "@metamask/keyring-api>@metamask/keyring-utils": true, + "@metamask/utils>@metamask/superstruct": true, + "@metamask/multichain-transactions-controller>@metamask/utils": true, + "@metamask/keyring-api>bech32": true + } + }, "@metamask/keyring-controller": { "packages": { "@ethereumjs/tx>@ethereumjs/util": true, @@ -1460,6 +1468,14 @@ "@metamask/keyring-snap-client>uuid": true } }, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client": { + "packages": { + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>@metamask/keyring-api": true, + "@metamask/keyring-api>@metamask/keyring-utils": true, + "@metamask/utils>@metamask/superstruct": true, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>uuid": true + } + }, "@metamask/keyring-api>@metamask/keyring-utils": { "globals": { "URL": true @@ -1518,6 +1534,27 @@ "lodash": true } }, + "@metamask/multichain-network-controller": { + "packages": { + "@metamask/base-controller": true, + "@metamask/keyring-api": true, + "@metamask/network-controller": true, + "@metamask/multichain-network-controller>@metamask/utils": true, + "@metamask/multichain-network-controller>@solana/addresses": true + } + }, + "@metamask/multichain-transactions-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/base-controller": true, + "@metamask/keyring-api": true, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client": true, + "@metamask/snaps-utils": true, + "@metamask/multichain-transactions-controller>@metamask/utils": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2397,6 +2434,36 @@ "semver": true } }, + "@metamask/multichain-network-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@noble/hashes": true, + "@metamask/utils>@scure/base": true, + "browserify>buffer": true, + "nock>debug": true, + "@metamask/utils>pony-cause": true, + "semver": true + } + }, + "@metamask/multichain-transactions-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@noble/hashes": true, + "@metamask/utils>@scure/base": true, + "browserify>buffer": true, + "nock>debug": true, + "@metamask/utils>pony-cause": true, + "semver": true + } + }, "@metamask/name-controller>@metamask/utils": { "globals": { "TextDecoder": true, @@ -2982,6 +3049,20 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses": { + "globals": { + "Intl.Collator": true, + "TextEncoder": true, + "crypto.subtle.digest": true, + "crypto.subtle.exportKey": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/assertions": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-strings": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/assertions": { "globals": { "crypto": true, @@ -2991,11 +3072,25 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/assertions": { + "globals": { + "crypto": true, + "isSecureContext": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/codecs-core": { "packages": { "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": { + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/codecs-strings": { "globals": { "TextDecoder": true, @@ -3008,11 +3103,28 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-strings": { + "globals": { + "TextDecoder": true, + "TextEncoder": true, + "atob": true, + "btoa": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/errors": { "globals": { "btoa": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": { + "globals": { + "btoa": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -3916,17 +4028,6 @@ "stream-browserify": true } }, - "ethers>@ethersproject/signing-key>elliptic": { - "packages": { - "bn.js": true, - "@metamask/ppom-validator>elliptic>brorand": true, - "ethers>@ethersproject/sha2>hash.js": true, - "@metamask/ppom-validator>elliptic>hmac-drbg": true, - "pumpify>inherits": true, - "@metamask/ppom-validator>elliptic>minimalistic-assert": true, - "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true - } - }, "@metamask/ppom-validator>elliptic": { "packages": { "bn.js": true, @@ -3938,17 +4039,6 @@ "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true } }, - "eth-lattice-keyring>gridplus-sdk>elliptic": { - "packages": { - "bn.js": true, - "@metamask/ppom-validator>elliptic>brorand": true, - "ethers>@ethersproject/sha2>hash.js": true, - "@metamask/ppom-validator>elliptic>hmac-drbg": true, - "pumpify>inherits": true, - "@metamask/ppom-validator>elliptic>minimalistic-assert": true, - "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true - } - }, "@metamask/eth-token-tracker>deep-equal>es-get-iterator": { "packages": { "string.prototype.matchall>call-bind": true, @@ -4266,7 +4356,7 @@ "ethereumjs-util>ethereum-cryptography>bs58check": true, "browserify>buffer": true, "@ethereumjs/tx>@ethereumjs/common>crc-32": true, - "eth-lattice-keyring>gridplus-sdk>elliptic": true, + "@metamask/ppom-validator>elliptic": true, "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": true, "ethers>@ethersproject/sha2>hash.js": true, "eth-ens-namehash>js-sha3": true, @@ -5896,6 +5986,11 @@ "crypto": true } }, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>uuid": { + "globals": { + "crypto": true + } + }, "eth-lattice-keyring>gridplus-sdk>uuid": { "globals": { "crypto": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index ae64f07624bb..7f6f58c9686d 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -412,7 +412,7 @@ "@ethersproject/bytes": true, "ethers>@ethersproject/logger": true, "ethers>@ethersproject/properties": true, - "ethers>@ethersproject/signing-key>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethers>@ethersproject/solidity": { @@ -1526,6 +1526,14 @@ "@metamask/keyring-api>bech32": true } }, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>@metamask/keyring-api": { + "packages": { + "@metamask/keyring-api>@metamask/keyring-utils": true, + "@metamask/utils>@metamask/superstruct": true, + "@metamask/multichain-transactions-controller>@metamask/utils": true, + "@metamask/keyring-api>bech32": true + } + }, "@metamask/keyring-controller": { "packages": { "@ethereumjs/tx>@ethereumjs/util": true, @@ -1552,6 +1560,14 @@ "@metamask/keyring-snap-client>uuid": true } }, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client": { + "packages": { + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>@metamask/keyring-api": true, + "@metamask/keyring-api>@metamask/keyring-utils": true, + "@metamask/utils>@metamask/superstruct": true, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>uuid": true + } + }, "@metamask/keyring-api>@metamask/keyring-utils": { "globals": { "URL": true @@ -1610,6 +1626,27 @@ "lodash": true } }, + "@metamask/multichain-network-controller": { + "packages": { + "@metamask/base-controller": true, + "@metamask/keyring-api": true, + "@metamask/network-controller": true, + "@metamask/multichain-network-controller>@metamask/utils": true, + "@metamask/multichain-network-controller>@solana/addresses": true + } + }, + "@metamask/multichain-transactions-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/base-controller": true, + "@metamask/keyring-api": true, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client": true, + "@metamask/snaps-utils": true, + "@metamask/multichain-transactions-controller>@metamask/utils": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2489,6 +2526,36 @@ "semver": true } }, + "@metamask/multichain-network-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@noble/hashes": true, + "@metamask/utils>@scure/base": true, + "browserify>buffer": true, + "nock>debug": true, + "@metamask/utils>pony-cause": true, + "semver": true + } + }, + "@metamask/multichain-transactions-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@noble/hashes": true, + "@metamask/utils>@scure/base": true, + "browserify>buffer": true, + "nock>debug": true, + "@metamask/utils>pony-cause": true, + "semver": true + } + }, "@metamask/name-controller>@metamask/utils": { "globals": { "TextDecoder": true, @@ -3074,6 +3141,20 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses": { + "globals": { + "Intl.Collator": true, + "TextEncoder": true, + "crypto.subtle.digest": true, + "crypto.subtle.exportKey": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/assertions": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-strings": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/assertions": { "globals": { "crypto": true, @@ -3083,11 +3164,25 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/assertions": { + "globals": { + "crypto": true, + "isSecureContext": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/codecs-core": { "packages": { "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": { + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/codecs-strings": { "globals": { "TextDecoder": true, @@ -3100,11 +3195,28 @@ "@solana/addresses>@solana/errors": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-strings": { + "globals": { + "TextDecoder": true, + "TextEncoder": true, + "atob": true, + "btoa": true + }, + "packages": { + "@metamask/multichain-network-controller>@solana/addresses>@solana/codecs-core": true, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": true + } + }, "@solana/addresses>@solana/errors": { "globals": { "btoa": true } }, + "@metamask/multichain-network-controller>@solana/addresses>@solana/errors": { + "globals": { + "btoa": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -4008,17 +4120,6 @@ "stream-browserify": true } }, - "ethers>@ethersproject/signing-key>elliptic": { - "packages": { - "bn.js": true, - "@metamask/ppom-validator>elliptic>brorand": true, - "ethers>@ethersproject/sha2>hash.js": true, - "@metamask/ppom-validator>elliptic>hmac-drbg": true, - "pumpify>inherits": true, - "@metamask/ppom-validator>elliptic>minimalistic-assert": true, - "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true - } - }, "@metamask/ppom-validator>elliptic": { "packages": { "bn.js": true, @@ -4030,17 +4131,6 @@ "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true } }, - "eth-lattice-keyring>gridplus-sdk>elliptic": { - "packages": { - "bn.js": true, - "@metamask/ppom-validator>elliptic>brorand": true, - "ethers>@ethersproject/sha2>hash.js": true, - "@metamask/ppom-validator>elliptic>hmac-drbg": true, - "pumpify>inherits": true, - "@metamask/ppom-validator>elliptic>minimalistic-assert": true, - "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true - } - }, "@metamask/eth-token-tracker>deep-equal>es-get-iterator": { "packages": { "string.prototype.matchall>call-bind": true, @@ -4358,7 +4448,7 @@ "ethereumjs-util>ethereum-cryptography>bs58check": true, "browserify>buffer": true, "@ethereumjs/tx>@ethereumjs/common>crc-32": true, - "eth-lattice-keyring>gridplus-sdk>elliptic": true, + "@metamask/ppom-validator>elliptic": true, "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": true, "ethers>@ethersproject/sha2>hash.js": true, "eth-ens-namehash>js-sha3": true, @@ -5988,6 +6078,11 @@ "crypto": true } }, + "@metamask/multichain-transactions-controller>@metamask/keyring-snap-client>uuid": { + "globals": { + "crypto": true + } + }, "eth-lattice-keyring>gridplus-sdk>uuid": { "globals": { "crypto": true diff --git a/package.json b/package.json index f715248a7422..a86a0d6c1d08 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,7 @@ "check-pr-has-required-labels": "ts-node ./.github/scripts/check-pr-has-required-labels.ts", "close-release-bug-report-issue": "ts-node ./.github/scripts/close-release-bug-report-issue.ts", "check-template-and-add-labels": "ts-node ./.github/scripts/check-template-and-add-labels.ts", + "create-bug-report-issue": "ts-node ./.github/scripts/create-bug-report-issue.ts", "audit": "yarn npm audit --recursive --environment production --severity moderate --ignore '@metamask/types (deprecation)'", "download-builds": "tsx .devcontainer/download-builds.ts prep-build", "download-builds:test": "tsx .devcontainer/download-builds.ts prep-build-test", @@ -259,7 +260,8 @@ "tslib@npm:^2.3.1": "~2.6.0", "tslib@npm:^2.4.0": "~2.6.0", "tslib@npm:^2.6.2": "~2.6.0", - "@metamask/providers@npm:^18.3.1": "patch:@metamask/providers@npm%3A19.0.0#~/.yarn/patches/@metamask-providers-npm-19.0.0-3d962c6f1a.patch" + "@ethersproject/signing-key/elliptic": "^6.6.1", + "gridplus-sdk/elliptic": "^6.6.1" }, "dependencies": { "@babel/runtime": "patch:@babel/runtime@npm%3A7.25.9#~/.yarn/patches/@babel-runtime-npm-7.25.9-fe8c62510a.patch", @@ -291,7 +293,7 @@ "@metamask-institutional/types": "^1.2.0", "@metamask/abi-utils": "^2.0.2", "@metamask/account-watcher": "^4.1.2", - "@metamask/accounts-controller": "^23.0.1", + "@metamask/accounts-controller": "^24.0.0", "@metamask/address-book-controller": "^6.0.3", "@metamask/announcement-controller": "^7.0.3", "@metamask/approval-controller": "^7.0.0", @@ -317,7 +319,7 @@ "@metamask/json-rpc-engine": "^10.0.0", "@metamask/json-rpc-middleware-stream": "^8.0.4", "@metamask/keyring-api": "^17.0.0", - "@metamask/keyring-controller": "^19.0.7", + "@metamask/keyring-controller": "^19.1.0", "@metamask/keyring-internal-api": "^4.0.2", "@metamask/keyring-snap-client": "^4.0.0", "@metamask/logging-controller": "^6.0.4", @@ -326,10 +328,11 @@ "@metamask/message-signing-snap": "^0.6.0", "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/multichain": "^2.1.0", + "@metamask/multichain-network-controller": "^0.1.0", "@metamask/multichain-transactions-controller": "^0.3.0", "@metamask/name-controller": "^8.0.3", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A22.1.1#~/.yarn/patches/@metamask-network-controller-npm-22.1.1-09b6510f1e.patch", - "@metamask/notification-services-controller": "^0.20.1", + "@metamask/notification-services-controller": "^0.21.0", "@metamask/object-multiplex": "^2.0.0", "@metamask/obs-store": "^9.0.0", "@metamask/permission-controller": "^11.0.6", @@ -340,7 +343,7 @@ "@metamask/ppom-validator": "0.36.0", "@metamask/preinstalled-example-snap": "^0.3.0", "@metamask/profile-sync-controller": "^7.0.1", - "@metamask/providers": "patch:@metamask/providers@npm%3A19.0.0#~/.yarn/patches/@metamask-providers-npm-19.0.0-3d962c6f1a.patch", + "@metamask/providers": "^20.0.0", "@metamask/queued-request-controller": "^7.0.1", "@metamask/rate-limit-controller": "^6.0.3", "@metamask/remote-feature-flag-controller": "^1.3.0", diff --git a/shared/constants/network.ts b/shared/constants/network.ts index 0358964b2661..1fab49ad25be 100644 --- a/shared/constants/network.ts +++ b/shared/constants/network.ts @@ -138,6 +138,7 @@ export const CHAIN_IDS = { CELO_TESTNET: '0xaef3', ZK_SYNC_ERA_TESTNET: '0x12c', MANTA_SEPOLIA: '0x138b', + UNICHAIN: '0x82', UNICHAIN_SEPOLIA: '0x515', LINEA_MAINNET: '0xe708', AURORA: '0x4e454152', @@ -519,6 +520,7 @@ export const SONIC_MAINNET_IMAGE_URL = './images/sonic.svg'; export const SONEIUM_IMAGE_URL = './images/soneium.svg'; export const MODE_SEPOLIA_IMAGE_URL = './images/mode-sepolia.svg'; export const MODE_IMAGE_URL = './images/mode.svg'; +export const UNICHAIN_IMAGE_URL = './images/unichain.svg'; export const INFURA_PROVIDER_TYPES = [ NETWORK_TYPES.MAINNET, @@ -883,6 +885,8 @@ export const CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP = { [CHAINLIST_CHAIN_IDS_MAP.SONEIUM_TESTNET]: SONEIUM_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.MODE_SEPOLIA]: MODE_SEPOLIA_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.MODE]: MODE_IMAGE_URL, + [CHAINLIST_CHAIN_IDS_MAP.UNICHAIN]: UNICHAIN_IMAGE_URL, + [CHAINLIST_CHAIN_IDS_MAP.UNICHAIN_SEPOLIA]: UNICHAIN_IMAGE_URL, } as const; export const CHAIN_ID_TO_ETHERS_NETWORK_NAME_MAP = { @@ -928,6 +932,8 @@ export const CHAIN_ID_TOKEN_IMAGE_MAP = { [CHAINLIST_CHAIN_IDS_MAP.SONIC_MAINNET]: SONIC_MAINNET_IMAGE_URL, [CHAIN_IDS.MODE]: ETH_TOKEN_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.FUNKICHAIN]: ETH_TOKEN_IMAGE_URL, + [CHAINLIST_CHAIN_IDS_MAP.UNICHAIN]: ETH_TOKEN_IMAGE_URL, + [CHAINLIST_CHAIN_IDS_MAP.UNICHAIN_SEPOLIA]: ETH_TOKEN_IMAGE_URL, } as const; export const INFURA_BLOCKED_KEY = 'countryBlocked'; diff --git a/shared/constants/price-api-currencies.ts b/shared/constants/price-api-currencies.ts new file mode 100644 index 000000000000..c600c227d86f --- /dev/null +++ b/shared/constants/price-api-currencies.ts @@ -0,0 +1,46 @@ +export const PRICE_API_CURRENCIES = [ + 'aud', + 'hkd', + 'sgd', + 'idr', + 'inr', + 'nzd', + 'php', + 'btc', + 'cad', + 'eur', + 'gbp', + 'jpy', + 'ltc', + 'rub', + 'uah', + 'usd', + 'xlm', + 'xrp', + 'sek', + 'aed', + 'ars', + 'bch', + 'bnb', + 'brl', + 'clp', + 'cny', + 'czk', + 'dkk', + 'chf', + 'dot', + 'eos', + 'eth', + 'gel', + 'huf', + 'ils', + 'krw', + 'mxn', + 'myr', + 'ngn', + 'nok', + 'pln', + 'thb', + 'try', + 'zar', +]; diff --git a/shared/constants/security-provider.ts b/shared/constants/security-provider.ts index 368d58db1ec4..0eca575ef942 100644 --- a/shared/constants/security-provider.ts +++ b/shared/constants/security-provider.ts @@ -1,9 +1,7 @@ -import { Hex } from '@metamask/utils'; import { SecurityAlertResponse, TransactionType, } from '@metamask/transaction-controller'; -import { CHAIN_IDS } from './network'; export enum SecurityProvider { Blockaid = 'blockaid', @@ -57,8 +55,6 @@ export enum BlockaidReason { errored = 'Error', notApplicable = 'NotApplicable', inProgress = 'validation_in_progress', - checkingChain = 'CheckingChain', - chainNotSupported = 'ChainNotSupported', } export enum BlockaidResultType { @@ -77,23 +73,6 @@ export const FALSE_POSITIVE_REPORT_BASE_URL = export const SECURITY_PROVIDER_UTM_SOURCE = 'metamask-ppom'; -export const SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS_FALLBACK_LIST: Hex[] = [ - CHAIN_IDS.ARBITRUM, - CHAIN_IDS.AVALANCHE, - CHAIN_IDS.BASE, - CHAIN_IDS.BSC, - CHAIN_IDS.LINEA_MAINNET, - CHAIN_IDS.MAINNET, - CHAIN_IDS.OPBNB, - CHAIN_IDS.OPTIMISM, - CHAIN_IDS.POLYGON, - CHAIN_IDS.SEPOLIA, - CHAIN_IDS.ZKSYNC_ERA, - CHAIN_IDS.SCROLL, - CHAIN_IDS.BERACHAIN, - CHAIN_IDS.METACHAIN_ONE, -]; - export const SECURITY_PROVIDER_EXCLUDED_TRANSACTION_TYPES = [ TransactionType.swap, TransactionType.swapApproval, @@ -107,17 +86,6 @@ export const LOADING_SECURITY_ALERT_RESPONSE: SecurityAlertResponse = { reason: BlockaidReason.inProgress, }; -export const SECURITY_ALERT_RESPONSE_CHECKING_CHAIN: SecurityAlertResponse = { - result_type: BlockaidResultType.Loading, - reason: BlockaidReason.checkingChain, -}; - -export const SECURITY_ALERT_RESPONSE_CHAIN_NOT_SUPPORTED: SecurityAlertResponse = - { - result_type: BlockaidResultType.Benign, - reason: BlockaidReason.chainNotSupported, - }; - export enum SecurityAlertSource { /** Validation performed remotely using the Security Alerts API. */ API = 'api', diff --git a/test/data/confirmations/typed_sign.ts b/test/data/confirmations/typed_sign.ts index b0984684f12d..efa65a05937d 100644 --- a/test/data/confirmations/typed_sign.ts +++ b/test/data/confirmations/typed_sign.ts @@ -203,7 +203,7 @@ export const seaportSignatureMsg = { networkClientId: 'mainnet', securityAlertResponse: { result_type: 'loading', - reason: 'CheckingChain', + reason: 'validation_in_progress', securityAlertId: 'def3b0ef-c96b-4c87-b1b1-c69cc02a0f78', }, status: 'unapproved', diff --git a/test/data/mock-accounts.ts b/test/data/mock-accounts.ts index 0ac8f31c27a4..932ed9b736f0 100644 --- a/test/data/mock-accounts.ts +++ b/test/data/mock-accounts.ts @@ -4,8 +4,11 @@ import { EthAccountType, BtcMethod, BtcAccountType, + SolAccountType, EthScope, BtcScope, + SolMethod, + SolScope, } from '@metamask/keyring-api'; import { ETH_EOA_METHODS, @@ -73,6 +76,21 @@ export const MOCK_ACCOUNT_BIP122_P2WPKH_TESTNET: InternalAccount = { }, }; +export const MOCK_ACCOUNT_SOLANA_MAINNET: InternalAccount = { + id: 'a3f9c2d4-6b8e-4d3a-9b2e-7f4b8e1a9c3d', + address: '3yZe7d5m8V9x2Q1w4u6t8b9n7k5j3h2g1f4d6s8a9p7q2r5t8v', + options: {}, + methods: [SolMethod.SendAndConfirmTransaction], + scopes: [SolScope.Mainnet], + type: SolAccountType.DataAccount, + metadata: { + name: 'Solana Account', + keyring: { type: KeyringTypes.snap }, + importTime: 1691592567600, + lastSelected: 1955565999999, + }, +}; + export const MOCK_ACCOUNTS = { [MOCK_ACCOUNT_EOA.id]: MOCK_ACCOUNT_EOA, [MOCK_ACCOUNT_ERC4337.id]: MOCK_ACCOUNT_ERC4337, diff --git a/test/e2e/changedFilesUtil.js b/test/e2e/changedFilesUtil.js index 0012a0faa170..145dcf6522b8 100644 --- a/test/e2e/changedFilesUtil.js +++ b/test/e2e/changedFilesUtil.js @@ -7,6 +7,7 @@ const CHANGED_FILES_PATH = path.join( 'changed-files', 'changed-files.txt', ); +const PR_INFO_PATH = path.join(BASE_PATH, 'changed-files', 'pr-body.txt'); /** * Reads the list of changed files from the git diff file with status (A, M, D). @@ -80,10 +81,49 @@ function getChangedAndNewFiles(changedFiles) { return changedFiles.map((file) => file.filePath); } +/** + * Checks if the E2E quality gate should be skipped based on the PR info. + * + * @returns {boolean} True if the quality gate should be skipped, otherwise false. + */ +function shouldE2eQualityGateBeSkipped() { + try { + const data = fs.readFileSync(PR_INFO_PATH, 'utf8'); + const lines = data.split('\n'); + const labelsLine = lines.find((line) => line.startsWith('PR labels:')); + const baseLine = lines.find((line) => line.startsWith('PR base:')); + + const labels = labelsLine + ? labelsLine + .replace(/PR labels: \{/gu, '') + .replace(/\}/gu, '') + .split(',') + .map((label) => label.trim()) + : []; + const base = baseLine + ? baseLine + .replace(/PR base: \{/gu, '') + .replace(/\}/gu, '') + .trim() + : ''; + console.log('PR labels', labels); + console.log('PR base', base); + + const skipGate = + labels.includes('skip-e2e-quality-gate') || base !== 'main'; + console.log('Should we skip the e2e quality gate:', skipGate); + return skipGate; + } catch (error) { + console.error('Error reading PR body file:', error); + return false; + } +} + module.exports = { filterE2eChangedFiles, getChangedAndNewFiles, getChangedFilesOnly, getNewFilesOnly, readChangedAndNewFilesWithStatus, + shouldE2eQualityGateBeSkipped, }; diff --git a/test/e2e/mock-e2e.js b/test/e2e/mock-e2e.js index d6d19d972d3b..748c00fcbd5c 100644 --- a/test/e2e/mock-e2e.js +++ b/test/e2e/mock-e2e.js @@ -1,8 +1,5 @@ const fs = require('fs'); -const { - SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS_FALLBACK_LIST, -} = require('../../shared/constants/security-provider'); const { BRIDGE_DEV_API_BASE_URL, BRIDGE_PROD_API_BASE_URL, @@ -161,15 +158,6 @@ async function setupMocking( }; }); - await server - .forGet(`${SECURITY_ALERTS_PROD_API_BASE_URL}/supportedChains`) - .thenCallback(() => { - return { - statusCode: 200, - json: SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS_FALLBACK_LIST, - }; - }); - await server .forPost(`${SECURITY_ALERTS_PROD_API_BASE_URL}/validate/${chainId}`) .thenCallback(() => { diff --git a/test/e2e/run-all.js b/test/e2e/run-all.js index b3a543f9d843..d3f400b121d3 100644 --- a/test/e2e/run-all.js +++ b/test/e2e/run-all.js @@ -10,6 +10,7 @@ const { filterE2eChangedFiles, getChangedAndNewFiles, readChangedAndNewFilesWithStatus, + shouldE2eQualityGateBeSkipped, } = require('./changedFilesUtil'); // These tests should only be run on Flask for now. @@ -75,10 +76,9 @@ function runningOnCircleCI(testPaths) { const changedOrNewTests = filterE2eChangedFiles(changedandNewFilesPaths); console.log('Changed or new test list:', changedOrNewTests); - const fullTestList = applyQualityGate( - testPaths.join('\n'), - changedOrNewTests, - ); + const fullTestList = shouldE2eQualityGateBeSkipped() + ? testPaths.join('\n') + : applyQualityGate(testPaths.join('\n'), changedOrNewTests); console.log('Full test list:', fullTestList); fs.writeFileSync('test/test-results/fullTestList.txt', fullTestList); diff --git a/test/e2e/tests/confirmations/signatures/signature-helpers.ts b/test/e2e/tests/confirmations/signatures/signature-helpers.ts index a4c9dd31b2ca..07cdc08e8c1a 100644 --- a/test/e2e/tests/confirmations/signatures/signature-helpers.ts +++ b/test/e2e/tests/confirmations/signatures/signature-helpers.ts @@ -39,6 +39,7 @@ type AssertSignatureMetricsOptions = { withAnonEvents?: boolean; securityAlertReason?: string; securityAlertResponse?: string; + securityAlertSource?: string; decodingChangeTypes?: string[]; decodingResponse?: string; decodingDescription?: string | null; @@ -52,6 +53,7 @@ type SignatureEventProperty = { locale: 'en'; security_alert_reason: string; security_alert_response: string; + security_alert_source?: string; signature_type: string; eip712_primary_type?: string; decoding_change_types?: string[]; @@ -83,6 +85,7 @@ export async function initializePages(driver: Driver) { * @param uiCustomizations * @param securityAlertReason * @param securityAlertResponse + * @param securityAlertSource * @param decodingChangeTypes * @param decodingResponse * @param decodingDescription @@ -91,8 +94,9 @@ function getSignatureEventProperty( signatureType: string, primaryType: string, uiCustomizations: string[], - securityAlertReason: string = BlockaidReason.checkingChain, + securityAlertReason: string = BlockaidReason.inProgress, securityAlertResponse: string = BlockaidResultType.Loading, + securityAlertSource: string = 'api', decodingChangeTypes?: string[], decodingResponse?: string, decodingDescription?: string | null, @@ -106,6 +110,7 @@ function getSignatureEventProperty( locale: 'en', security_alert_reason: securityAlertReason, security_alert_response: securityAlertResponse, + security_alert_source: securityAlertSource, ui_customizations: uiCustomizations, }; @@ -118,6 +123,7 @@ function getSignatureEventProperty( signatureEventProperty.decoding_response = decodingResponse; signatureEventProperty.decoding_description = decodingDescription; } + return signatureEventProperty; } @@ -150,6 +156,7 @@ export async function assertSignatureConfirmedMetrics({ withAnonEvents = false, securityAlertReason, securityAlertResponse, + securityAlertSource, decodingChangeTypes, decodingResponse, decodingDescription, @@ -161,6 +168,7 @@ export async function assertSignatureConfirmedMetrics({ uiCustomizations, securityAlertReason, securityAlertResponse, + securityAlertSource, decodingChangeTypes, decodingResponse, decodingDescription, @@ -197,6 +205,7 @@ export async function assertSignatureRejectedMetrics({ withAnonEvents = false, securityAlertReason, securityAlertResponse, + securityAlertSource, decodingChangeTypes, decodingResponse, decodingDescription, @@ -208,6 +217,7 @@ export async function assertSignatureRejectedMetrics({ uiCustomizations, securityAlertReason, securityAlertResponse, + securityAlertSource, decodingChangeTypes, decodingResponse, decodingDescription, @@ -264,7 +274,7 @@ function assertEventPropertiesMatch( compareDecodingAPIResponse(actualProperties, expectedProps, eventName); - compareSecurityAlertResponse(actualProperties, expectedProps, eventName); + compareSecurityAlertProperties(actualProperties, expectedProps, eventName); assert(event, `${eventName} event not found`); assert.deepStrictEqual( @@ -274,7 +284,7 @@ function assertEventPropertiesMatch( ); } -function compareSecurityAlertResponse( +function compareSecurityAlertProperties( actualProperties: Record, expectedProperties: Record, eventName: string, @@ -296,6 +306,19 @@ function compareSecurityAlertResponse( delete actualProperties.security_alert_response; delete expectedProperties.security_alert_response; } + + if (expectedProperties.security_alert_source) { + if ( + actualProperties.security_alert_source !== 'api' && + expectedProperties.security_alert_source !== 'api' + ) { + assert.fail( + `${eventName} event properties do not match: security_alert_source is ${actualProperties.security_alert_source}`, + ); + } + delete actualProperties.security_alert_source; + delete expectedProperties.security_alert_source; + } } function compareDecodingAPIResponse( diff --git a/test/e2e/tests/metrics/errors.spec.js b/test/e2e/tests/metrics/errors.spec.js index cbe96504a50a..ff930544ce7c 100644 --- a/test/e2e/tests/metrics/errors.spec.js +++ b/test/e2e/tests/metrics/errors.spec.js @@ -892,6 +892,7 @@ describe('Sentry errors', function () { balances: false, accountsAssets: false, assetsMetadata: false, + assetsRates: false, smartTransactionsState: { fees: { approvalTxFees: true, // Initialized as undefined diff --git a/test/e2e/tests/metrics/segment-user-traits.spec.ts b/test/e2e/tests/metrics/segment-user-traits.spec.ts index ba53a47239ff..45481d27af6e 100644 --- a/test/e2e/tests/metrics/segment-user-traits.spec.ts +++ b/test/e2e/tests/metrics/segment-user-traits.spec.ts @@ -164,7 +164,6 @@ describe('Segment User Traits', function () { const privacySettings = new PrivacySettings(driver); await privacySettings.check_pageIsLoaded(); - await privacySettings.toggleParticipateInMetaMetrics(); await privacySettings.toggleDataCollectionForMarketing(); events = await getEventPayloads(driver, mockedEndpoints); assert.equal(events.length, 1); diff --git a/test/e2e/tests/metrics/signature-approved.spec.js b/test/e2e/tests/metrics/signature-approved.spec.js index 16001840360e..4ea14d783a9b 100644 --- a/test/e2e/tests/metrics/signature-approved.spec.js +++ b/test/e2e/tests/metrics/signature-approved.spec.js @@ -51,7 +51,7 @@ const expectedEventPropertiesBase = { locale: 'en', chain_id: '0x539', environment_type: 'background', - security_alert_reason: 'CheckingChain', + security_alert_reason: 'validation_in_progress', security_alert_response: 'loading', ui_customizations: ['redesigned_confirmation'], }; @@ -92,6 +92,7 @@ describe('Signature Approved Event', function () { signature_type: 'eth_signTypedData_v4', eip712_primary_type: 'Mail', security_alert_response: 'Benign', + security_alert_source: 'api', }); }, ); @@ -130,6 +131,7 @@ describe('Signature Approved Event', function () { ...expectedEventPropertiesBase, signature_type: 'eth_signTypedData_v3', security_alert_response: 'Benign', + security_alert_source: 'api', }); }, ); @@ -168,6 +170,7 @@ describe('Signature Approved Event', function () { ...expectedEventPropertiesBase, signature_type: 'eth_signTypedData', security_alert_response: 'Benign', + security_alert_source: 'api', }); }, ); @@ -206,6 +209,7 @@ describe('Signature Approved Event', function () { ...expectedEventPropertiesBase, signature_type: 'personal_sign', security_alert_response: 'Benign', + security_alert_source: 'api', }); }, ); 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 2e84b6944a18..f62e291abfd0 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 @@ -143,6 +143,7 @@ "metaMetricsDataDeletionId": null, "metaMetricsDataDeletionTimestamp": 0 }, + "MultichainNetworkController": "object", "MultichainRatesController": { "fiatCurrency": "usd", "rates": { 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 8446d437617f..0c193a44a642 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 @@ -157,6 +157,9 @@ "previousAppVersion": "", "previousMigrationVersion": 0, "currentMigrationVersion": "number", + "multichainNetworkConfigurationsByChainId": "object", + "selectedMultichainNetworkChainId": "string", + "isEvmSelected": "boolean", "selectedNetworkClientId": "string", "networksMetadata": { "networkConfigurationId": { diff --git a/test/e2e/webdriver/chrome.js b/test/e2e/webdriver/chrome.js index fa56c107439e..32a55bfc6dee 100644 --- a/test/e2e/webdriver/chrome.js +++ b/test/e2e/webdriver/chrome.js @@ -61,7 +61,7 @@ class ChromeDriver { args.push('--disable-gpu'); } - if (isHeadless('SELENIUM')) { + if (process.env.GITHUB_ACTION || isHeadless('SELENIUM')) { // TODO: Remove notice and consider non-experimental when results are consistent console.warn( '*** Running e2e tests in headless mode is experimental and some tests are known to fail for unknown reasons', diff --git a/test/e2e/webdriver/driver.js b/test/e2e/webdriver/driver.js index 1ec1f195c354..6eb3390af5ed 100644 --- a/test/e2e/webdriver/driver.js +++ b/test/e2e/webdriver/driver.js @@ -704,7 +704,7 @@ class Driver { * @param rawLocator - Element locator * @param timeout - The maximum time in ms to wait for the element */ - async clickElementSafe(rawLocator, timeout = 1000) { + async clickElementSafe(rawLocator, timeout = 2000) { try { const locator = this.buildLocator(rawLocator); const elements = await this.driver.wait( diff --git a/ui/components/component-library/modal-content/modal-content.scss b/ui/components/component-library/modal-content/modal-content.scss index 6ecc79083da5..e1ba1e132e2b 100644 --- a/ui/components/component-library/modal-content/modal-content.scss +++ b/ui/components/component-library/modal-content/modal-content.scss @@ -5,8 +5,6 @@ left: 0; top: 0; z-index: design-system.$modal-z-index; - overflow: auto; - overscroll-behavior-y: none; // Maximize dialog visibility on small screen heights @media (max-height: 475px) { @@ -71,11 +69,13 @@ from { transform: translateY(24px); opacity: 0; + overflow: hidden; } to { transform: translateY(0); opacity: 1; + overflow: hidden; } } } diff --git a/ui/components/component-library/modal-content/modal-content.stories.tsx b/ui/components/component-library/modal-content/modal-content.stories.tsx index 4dfb10455e86..cbe39bebe1a0 100644 --- a/ui/components/component-library/modal-content/modal-content.stories.tsx +++ b/ui/components/component-library/modal-content/modal-content.stories.tsx @@ -61,7 +61,7 @@ export const DefaultStory: StoryFn = (args) => { Modal Header - Modal Content + Modal Body ( ); const Template: StoryFn = (args) => { - const [{ isOpen }, updateArgs] = useArgs(); - const [showLoremIpsum, setShowLoremIpsum] = useState(false); - const [showMoreModalContent, setShowMoreModalContent] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const [showLoremIpsum, setShowLoremIpsum] = useState(true); + const [showMoreModalContent, setShowMoreModalContent] = useState(true); + const handleOnClick = () => { - updateArgs({ isOpen: true }); + setIsOpen(true); }; const handleOnClose = () => { - updateArgs({ isOpen: false }); + setIsOpen(false); }; const handleHideLoremIpsum = () => { setShowLoremIpsum(!showLoremIpsum); @@ -184,13 +184,16 @@ IsClosedOnEscapeKey.args = { export const InitialFocusRef: StoryFn = (args) => { const inputRef = React.useRef(null); - const [{ isOpen }, updateArgs] = useArgs(); + const [isOpen, setIsOpen] = useState(false); + const handleOnClick = () => { - updateArgs({ isOpen: true }); + setIsOpen(true); }; + const handleOnClose = () => { - updateArgs({ isOpen: false }); + setIsOpen(false); }; + return ( <> @@ -232,13 +235,16 @@ InitialFocusRef.args = { export const FinalFocusRef: StoryFn = (args) => { const buttonRef = React.useRef(null); - const [{ isOpen }, updateArgs] = useArgs(); + const [isOpen, setIsOpen] = useState(false); + const handleOnClick = () => { - updateArgs({ isOpen: true }); + setIsOpen(true); }; + const handleOnClose = () => { - updateArgs({ isOpen: false }); + setIsOpen(false); }; + return ( <>