1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-22 17:33:23 +01:00

Merge branch 'develop' of github.com:MetaMask/metamask-extension into minimal

This commit is contained in:
Matthias Kretschmann 2023-06-29 13:54:53 +01:00
commit 4988e033ad
Signed by: m
GPG Key ID: 606EEEF3C479A91F
221 changed files with 4461 additions and 2767 deletions

View File

@ -30,11 +30,44 @@ rc_branch_only: &rc_branch_only
only:
- /^Version-v(\d+)[.](\d+)[.](\d+)/
rc_or_master_branch_only: &rc_or_master_branch_only
filters:
branches:
only:
- /^Version-v(\d+)[.](\d+)[.](\d+)|master/
aliases:
# Shallow Git Clone
- &shallow-git-clone
name: Shallow Git Clone
command: |
#!/bin/bash
set -e
set -u
set -o pipefail
# This Shallow Git Clone code is adapted from what the standard CircleCI `checkout` command does for the case of an external PR (link to example below):
# https://app.circleci.com/pipelines/github/MetaMask/metamask-extension/49817/workflows/dc195ea6-ac06-4de1-9edf-4c949427b5fb/jobs/1430976/parallel-runs/0/steps/0-101
### git clone --no-checkout "$CIRCLE_REPOSITORY_URL" .
### git fetch --force origin +refs/pull/18748/head:refs/remotes/origin/pull/18748
### git checkout --force -B "$CIRCLE_BRANCH" "$CIRCLE_SHA1"
### git --no-pager log --no-color -n 1 --format='HEAD is now at %h %s'
# Set up SSH access
# This SSH key is the current github.com SSH key as of June 2023, but it will need to be changed whenever github changes their key (probably every few years)
GITHUB_SSH_KEY="AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl"
mkdir -p ~/.ssh
echo github.com ssh-ed25519 $GITHUB_SSH_KEY >> ~/.ssh/known_hosts
# Take a different clone path depending on if it's a tag, a PR from an external repo, or the normal case
if [ -n "${CIRCLE_TAG-}" ]; then
# tag
git clone --depth 1 --no-checkout "$CIRCLE_REPOSITORY_URL" .
git fetch --depth 1 --force origin "+refs/tags/${CIRCLE_TAG}:refs/tags/${CIRCLE_TAG}"
git checkout --force -q "$CIRCLE_TAG" "$CIRCLE_SHA1"
elif [[ "$CIRCLE_BRANCH" =~ ^pull\/* ]]; then
# pull request
git clone --depth 1 --no-checkout "$CIRCLE_REPOSITORY_URL" .
git fetch --depth 1 --force origin "${CIRCLE_BRANCH}/head:remotes/origin/${CIRCLE_BRANCH}"
git checkout --force -B "$CIRCLE_BRANCH" "$CIRCLE_SHA1"
else
# normal case
git clone --depth 1 "$CIRCLE_REPOSITORY_URL" --branch "$CIRCLE_BRANCH" .
fi
workflows:
test_and_release:
@ -57,15 +90,12 @@ workflows:
requires:
- prep-deps
- validate-lavamoat-allow-scripts:
<<: *rc_or_master_branch_only
requires:
- prep-deps
- validate-lavamoat-policy-build:
<<: *rc_or_master_branch_only
requires:
- prep-deps
- validate-lavamoat-policy-webapp:
<<: *rc_or_master_branch_only
matrix:
parameters:
build-type: [main, beta, flask, mmi, desktop]
@ -250,7 +280,7 @@ jobs:
trigger-beta-build:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- when:
@ -279,7 +309,7 @@ jobs:
create_release_pull_request:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -298,7 +328,7 @@ jobs:
prep-deps:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- restore_cache:
keys:
# First try to get the specific cache for the checksum of the yarn.lock file.
@ -348,7 +378,7 @@ jobs:
validate-lavamoat-allow-scripts:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -361,7 +391,7 @@ jobs:
validate-lavamoat-policy-build:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -377,7 +407,7 @@ jobs:
build-type:
type: string
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -390,7 +420,7 @@ jobs:
prep-build:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- when:
@ -424,7 +454,7 @@ jobs:
prep-build-desktop:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -448,7 +478,7 @@ jobs:
prep-build-flask:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- when:
@ -488,7 +518,7 @@ jobs:
prep-build-test-flask:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -509,7 +539,7 @@ jobs:
prep-build-test-mv3:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -530,7 +560,7 @@ jobs:
prep-build-test:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -551,7 +581,7 @@ jobs:
prep-build-storybook:
executor: node-browsers-large
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -565,7 +595,7 @@ jobs:
prep-build-ts-migration-dashboard:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -579,7 +609,7 @@ jobs:
test-yarn-dedupe:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -589,7 +619,7 @@ jobs:
test-lint:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -602,7 +632,7 @@ jobs:
test-storybook:
executor: node-browsers-large
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -624,7 +654,7 @@ jobs:
test-lint-lockfile:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -634,7 +664,7 @@ jobs:
test-lint-changelog:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- when:
@ -660,7 +690,7 @@ jobs:
test-deps-audit:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -670,7 +700,7 @@ jobs:
test-deps-depcheck:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -681,7 +711,7 @@ jobs:
executor: node-browsers
parallelism: 8
steps:
- checkout
- run: *shallow-git-clone
- run:
name: Re-Install Chrome
command: ./.circleci/scripts/chrome-install.sh
@ -718,7 +748,7 @@ jobs:
executor: node-browsers
parallelism: 8
steps:
- checkout
- run: *shallow-git-clone
- run:
name: Re-Install Chrome
command: ./.circleci/scripts/chrome-install.sh
@ -746,7 +776,7 @@ jobs:
executor: node-browsers
parallelism: 4
steps:
- checkout
- run: *shallow-git-clone
- run:
name: Install Firefox
command: ./.circleci/scripts/firefox-install.sh
@ -783,7 +813,7 @@ jobs:
executor: node-browsers
parallelism: 4
steps:
- checkout
- run: *shallow-git-clone
- run:
name: Re-Install Chrome
command: ./.circleci/scripts/chrome-install.sh
@ -820,7 +850,7 @@ jobs:
executor: node-browsers-medium-plus
parallelism: 8
steps:
- checkout
- run: *shallow-git-clone
- run:
name: Install Firefox
command: ./.circleci/scripts/firefox-install.sh
@ -856,7 +886,7 @@ jobs:
benchmark:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- run:
name: Re-Install Chrome
command: ./.circleci/scripts/chrome-install.sh
@ -882,7 +912,7 @@ jobs:
user-actions-benchmark:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- run:
name: Re-Install Chrome
command: ./.circleci/scripts/chrome-install.sh
@ -908,7 +938,7 @@ jobs:
stats-module-load-init:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- run:
name: Re-Install Chrome
command: ./.circleci/scripts/chrome-install.sh
@ -1005,7 +1035,7 @@ jobs:
job-publish-release:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1025,7 +1055,7 @@ jobs:
- add_ssh_keys:
fingerprints:
- '3d:49:29:f4:b2:e8:ea:af:d1:32:eb:2a:fc:15:85:d8'
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1040,7 +1070,7 @@ jobs:
- add_ssh_keys:
fingerprints:
- '8b:21:e3:20:7c:c9:db:82:74:2d:86:d6:11:a7:2f:49'
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1054,7 +1084,7 @@ jobs:
test-unit-mocha:
executor: node-browsers-medium-plus
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1069,7 +1099,7 @@ jobs:
test-unit-jest-development:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1086,7 +1116,7 @@ jobs:
executor: node-browsers-medium-plus
parallelism: 12
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1102,7 +1132,7 @@ jobs:
upload-and-validate-coverage:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- codecov/upload
@ -1117,7 +1147,7 @@ jobs:
test-unit-global:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1127,7 +1157,7 @@ jobs:
validate-source-maps:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1137,7 +1167,7 @@ jobs:
validate-source-maps-beta:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1148,7 +1178,7 @@ jobs:
validate-source-maps-desktop:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1164,7 +1194,7 @@ jobs:
validate-source-maps-flask:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1180,7 +1210,7 @@ jobs:
test-mozilla-lint:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1190,7 +1220,7 @@ jobs:
test-mozilla-lint-desktop:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:
@ -1206,7 +1236,7 @@ jobs:
test-mozilla-lint-flask:
executor: node-browsers
steps:
- checkout
- run: *shallow-git-clone
- attach_workspace:
at: .
- run:

View File

@ -1,177 +0,0 @@
import * as core from '@actions/core';
import { context, getOctokit } from '@actions/github';
import { GitHub } from '@actions/github/lib/utils';
main().catch((error: Error): void => {
console.error(error);
process.exit(1);
});
async function main(): Promise<void> {
const token = process.env.GITHUB_TOKEN;
if (!token) {
core.setFailed('GITHUB_TOKEN not found');
process.exit(1);
}
const octokit = getOctokit(token);
const headRef = context.payload.pull_request?.head.ref || '';
let issueNumber = await getIssueNumberFromPullRequestBody();
if (issueNumber === "") {
bailIfIsBranchNameInvalid(headRef);
bailIfIsNotFeatureBranch(headRef);
issueNumber = getIssueNumberFromBranchName(headRef);
}
if (Number(issueNumber) === 0) {
process.exit(0);
}
await updateLabels(octokit, issueNumber);
}
async function getIssueNumberFromPullRequestBody(): Promise<string> {
console.log("Checking if the PR's body references an issue...");
let ISSUE_LINK_IN_PR_DESCRIPTION_REGEX =
/(close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s#\d+/gi;
const prBody = await getPullRequestBody();
let matches = prBody.match(ISSUE_LINK_IN_PR_DESCRIPTION_REGEX);
if (!matches || matches?.length === 0) {
console.log(
'No direct link can be drawn between the PR and an issue from the PR body because no issue number was referenced.',
);
return "";
}
if (matches?.length > 1) {
console.log(
'No direct link can be drawn between the PR and an issue from the PR body because more than one issue number was referenced.',
);
return "";
}
const ISSUE_NUMBER_REGEX = /\d+/;
const issueNumber = matches[0].match(ISSUE_NUMBER_REGEX)?.[0] || '';
console.log(`Found issue number ${issueNumber} in PR body.`);
return issueNumber;
}
async function getPullRequestBody(): Promise<string> {
if (context.eventName !== 'pull_request') {
console.log('This action should only run on pull_request events.');
process.exit(1);
}
const prBody = context.payload.pull_request?.body || '';
return prBody;
}
function bailIfIsBranchNameInvalid(branchName: string): void {
const BRANCH_REGEX =
/^(main|develop|(ci|chore|docs|feat|feature|fix|perf|refactor|revert|style)\/\d*(?:[-](?![-])\w*)*|Version-v\d+\.\d+\.\d+)$/;
const isValidBranchName = new RegExp(BRANCH_REGEX).test(branchName);
if (!isValidBranchName) {
console.log('This branch name does not follow the convention.');
console.log(
'Here are some example branch names that are accepted: "fix/123-description", "feat/123-longer-description", "feature/123", "main", "develop", "Version-v10.24.2".',
);
console.log(
'No issue could be linked to this PR, so no labels were copied',
);
process.exit(0);
}
}
function bailIfIsNotFeatureBranch(branchName: string): void {
if (
branchName === 'main' ||
branchName === 'develop' ||
branchName.startsWith('Version-v')
) {
console.log(`${branchName} is not a feature branch.`);
console.log(
'No issue could be linked to this PR, so no labels were copied',
);
process.exit(0);
}
}
async function updateLabels(octokit: InstanceType<typeof GitHub>, issueNumber: string): Promise<void> {
interface ILabel {
name: string;
};
const owner = context.repo.owner;
const repo = context.repo.repo;
const issue = await octokit.rest.issues.get({
owner: owner,
repo: repo,
issue_number: Number(issueNumber),
});
const getNameFromLabel = (label: ILabel): string => label.name
const issueLabels = issue.data.labels.map(label => getNameFromLabel(label as ILabel));
const prNumber = context.payload.number;
const pr = await octokit.rest.issues.get({
owner: owner,
repo: repo,
issue_number: prNumber,
});
const startingPRLabels = pr.data.labels.map(label => getNameFromLabel(label as ILabel));
const dedupedFinalPRLabels = [
...new Set([...startingPRLabels, ...issueLabels]),
];
const hasIssueAdditionalLabels = !sortedArrayEqual(
startingPRLabels,
dedupedFinalPRLabels,
);
if (hasIssueAdditionalLabels) {
await octokit.rest.issues.update({
owner,
repo,
issue_number: prNumber,
labels: dedupedFinalPRLabels,
});
}
}
function getIssueNumberFromBranchName(branchName: string): string {
console.log('Checking if the branch name references an issue...');
let issueNumber: string;
if (branchName.split('/').length > 1) {
issueNumber = branchName.split('/')[1].split('-')[0];
} else {
issueNumber = branchName.split('-')[0];
}
console.log(`Found issue number ${issueNumber} in branch name.`);
return issueNumber;
}
function sortedArrayEqual(array1: string[], array2: string[]): boolean {
const lengthsAreEqual = array1.length === array2.length;
const everyElementMatchesByIndex = array1.every(
(value: string, index: number): boolean => value === array2[index],
);
return lengthsAreEqual && everyElementMatchesByIndex;
}

View File

@ -21,6 +21,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
cache: yarn
- name: Install dependencies
run: yarn --immutable

View File

@ -18,6 +18,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
cache: yarn
- name: Install dependencies
run: yarn --immutable

View File

@ -1,27 +0,0 @@
name: Label PR
on:
pull_request:
types: [assigned, opened, edited, synchronize, reopened]
jobs:
label-pr:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install Yarn
run: npm install -g yarn
- name: Install dependencies
run: yarn

View File

@ -9,6 +9,9 @@ on:
jobs:
cleanup:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Remove labels

View File

@ -20,6 +20,28 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
react-to-comment:
name: React to the comment
runs-on: ubuntu-latest
needs: is-fork-pull-request
# Early exit if this is a fork, since later steps are skipped for forks
if: ${{ needs.is-fork-pull-request.outputs.IS_FORK == 'false' }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: React to the comment
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" \
-f content='+1'
env:
COMMENT_ID: ${{ github.event.comment.id }}
GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }}
REPO: ${{ github.repository }}
prepare:
name: Prepare dependencies
runs-on: ubuntu-latest
@ -27,7 +49,13 @@ jobs:
# Early exit if this is a fork, since later steps are skipped for forks
if: ${{ needs.is-fork-pull-request.outputs.IS_FORK == 'false' }}
steps:
- uses: actions/checkout@v3
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout pull request
run: gh pr checkout "${PR_NUMBER}"
env:
GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
- name: Use Node.js
uses: actions/setup-node@v3
with:
@ -44,6 +72,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout pull request
run: gh pr checkout "${PR_NUMBER}"
env:
GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
@ -72,6 +105,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout pull request
run: gh pr checkout "${PR_NUMBER}"
env:
GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
@ -99,7 +137,8 @@ jobs:
# Ensure forks don't get access to the LavaMoat update token
if: ${{ needs.is-fork-pull-request.outputs.IS_FORK == 'false' }}
steps:
- uses: actions/checkout@v3
- name: Checkout repository
uses: actions/checkout@v3
with:
# Use PAT to ensure that the commit later can trigger status check workflows
token: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }}
@ -113,6 +152,7 @@ jobs:
with:
path: lavamoat/build-system
key: cache-build-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
# One restore step per build type: [main, beta, flask, mmi, desktop]
# Ensure this is synchronized with the list above in the "update-lavamoat-webapp-policy" job
# and with the build type list in `builds.yml`
@ -121,27 +161,31 @@ jobs:
with:
path: lavamoat/browserify/main
key: cache-main-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Restore beta application policy
uses: actions/cache/restore@v3
with:
path: lavamoat/browserify/beta
key: cache-beta-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Restore flask application policy
uses: actions/cache/restore@v3
with:
path: lavamoat/browserify/flask
key: cache-flask-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Restore mmi application policy
uses: actions/cache/restore@v3
with:
path: lavamoat/browserify/mmi
key: cache-mmi-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Restore desktop application policy
uses: actions/cache/restore@v3
with:
path: lavamoat/browserify/desktop
key: cache-desktop-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Check whether there are policy changes
id: policy-changes
run: |

View File

@ -158,7 +158,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Additional incoming transactions support ([#14219](https://github.com/MetaMask/metamask-extension/pull/14219))
### Changed
- UX: Loaclize the avatar-favicon description text ([#18132](https://github.com/MetaMask/metamask-extension/pull/18132))
- UX: Localize the avatar-favicon description text ([#18132](https://github.com/MetaMask/metamask-extension/pull/18132))
- 17921 Update TransactionAlerts with BannerAlert ([#17940](https://github.com/MetaMask/metamask-extension/pull/17940))
- Part of 17670: Replace Typography with Text confirm-approve-content.component.js and home.component.js ([#18049](https://github.com/MetaMask/metamask-extension/pull/18049))
- UX: Icon: Update buy icon ([#18123](https://github.com/MetaMask/metamask-extension/pull/18123))

View File

@ -202,9 +202,6 @@
"delete": {
"message": "ሰርዝ"
},
"deleteAccount": {
"message": "መለያን ሰርዝ"
},
"deleteNetwork": {
"message": "አውታረ መረብ ይሰረዝ?"
},

View File

@ -215,9 +215,6 @@
"delete": {
"message": "حذف"
},
"deleteAccount": {
"message": "حذف الحساب"
},
"deleteNetwork": {
"message": "هل تريد حذف الشبكة؟"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Изтриване"
},
"deleteAccount": {
"message": "Изтриване на акаунт"
},
"deleteNetwork": {
"message": "Да се изтрие ли мрежата?"
},

View File

@ -208,9 +208,6 @@
"delete": {
"message": "মুছুন"
},
"deleteAccount": {
"message": "অ্যাকাউন্ট মুছুন"
},
"deleteNetwork": {
"message": "নেটওয়ার্ক মুছবেন?"
},

View File

@ -208,9 +208,6 @@
"delete": {
"message": "Suprimeix"
},
"deleteAccount": {
"message": "Elimina el compte"
},
"deleteNetwork": {
"message": "Esborrar Xarxa?"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Slet"
},
"deleteAccount": {
"message": "Slet konto"
},
"deleteNetwork": {
"message": "Slet Netværk?"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "Löschen"
},
"deleteAccount": {
"message": "Konto löschen"
},
"deleteNetwork": {
"message": "Netzwerk löschen?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "Die ID eines NFTs ist eine eindeutige Kennung, da keine zwei NFTs gleich sind. Auch diese Nummer finden Sie in OpenSea unter „Details“. Notieren Sie diese oder kopieren Sie sie in Ihre Zwischenablage."
},
"importNFTs": {
"message": "NFTs importieren"
},
"importSelectedTokens": {
"message": "Ausgewählte Token importieren?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "Laut unseren Aufzeichnungen stimmt der angegebene RPC-URL-Wert nicht mit einem bekannten Provider für diese Chain-ID überein."
},
"missingNFT": {
"message": "Sie sehen Ihr NFT nicht?"
},
"missingSetting": {
"message": "Sie können eine Einstellung nicht finden?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "Kein Umrechnungskurs verfügbar"
},
"noNFTs": {
"message": "Noch keine NFTs"
},
"noSnaps": {
"message": "Keine Snaps installiert"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "Διαγραφή"
},
"deleteAccount": {
"message": "Διαγραφή Λογαριασμού"
},
"deleteNetwork": {
"message": "Διαγραφή Δικτύου;"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "Ένα αναγνωριστικό του συλλεκτικού είναι ένα μοναδικό αναγνωριστικό δεδομένου ότι δεν υπάρχουν δύο ίδια NFT. Και πάλι, στο OpenSea αυτός ο αριθμός βρίσκεται στην ενότητα \"Λεπτομέρειες\". Σημειώστε τον ή αντιγράψτε τον στο πρόχειρο σας."
},
"importNFTs": {
"message": "Εισαγωγή NFT"
},
"importSelectedTokens": {
"message": "Εισαγωγή επιλεγμένων token;"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "Σύμφωνα με τις καταχωρήσεις μας, η τιμή RPC URL που υποβλήθηκε δεν ταιριάζει με κάποιον γνωστό πάροχο για αυτό το αναγνωριστικό αλυσίδας."
},
"missingNFT": {
"message": "Δεν βλέπετε το NFT σας;"
},
"missingSetting": {
"message": "Δεν μπορείτε να βρείτε μια ρύθμιση;"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "Δεν Υπάρχει Διαθέσιμη Ισοτιμία Μετατροπής"
},
"noNFTs": {
"message": "Δεν υπάρχουν NFT ακόμα"
},
"noSnaps": {
"message": "Δεν εγκαταστάθηκαν Snaps"
},

View File

@ -1101,8 +1101,8 @@
"delete": {
"message": "Delete"
},
"deleteAccount": {
"message": "Delete account"
"deleteContact": {
"message": "Delete contact"
},
"deleteNetwork": {
"message": "Delete network?"
@ -1884,9 +1884,6 @@
"importNFTTokenIdToolTip": {
"message": "An NFT's ID is a unique identifier since no two NFTs are alike. Again, on OpenSea this number is under 'Details'. Make a note of it, or copy it onto your clipboard."
},
"importNFTs": {
"message": "Import NFTs"
},
"importSelectedTokens": {
"message": "Import selected tokens?"
},
@ -2273,9 +2270,6 @@
"mismatchedRpcUrl": {
"message": "According to our records the submitted RPC URL value does not match a known provider for this chain ID."
},
"missingNFT": {
"message": "Don't see your NFT?"
},
"missingSetting": {
"message": "Can't find a setting?"
},
@ -5162,6 +5156,9 @@
"viewOnOpensea": {
"message": "View on Opensea"
},
"viewPortfolioDashboard": {
"message": "View Portfolio Dashboard"
},
"viewinCustodianApp": {
"message": "View in custodian app"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "Eliminar"
},
"deleteAccount": {
"message": "Eliminar cuenta"
},
"deleteNetwork": {
"message": "¿Eliminar red?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "La ID de un NFT es un identificador único, ya que no hay dos NFT iguales. Nuevamente, en OpenSea, este número se encuentra en 'Detalles'. Tome nota de ello o cópielo en su portapapeles."
},
"importNFTs": {
"message": "AGREGAR NFT"
},
"importSelectedTokens": {
"message": "¿Agregar los tokens seleccionados?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "Según nuestros registros, el valor de la URL de RPC enviado no coincide con un proveedor conocido para este ID de cadena."
},
"missingNFT": {
"message": "¿No ve su NFT?"
},
"missingSetting": {
"message": "¿No puede encontrar un ajuste?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "No hay tasa de conversión disponible"
},
"noNFTs": {
"message": "No hay ningún NFT aún"
},
"noSnaps": {
"message": "No hay complementos instalados"
},

View File

@ -581,9 +581,6 @@
"delete": {
"message": "Eliminar"
},
"deleteAccount": {
"message": "Eliminar cuenta"
},
"deleteNetwork": {
"message": "¿Eliminar red?"
},
@ -1073,9 +1070,6 @@
"importMyWallet": {
"message": "Importar Mi cartera"
},
"importNFTs": {
"message": "Importar NFT"
},
"importTokenQuestion": {
"message": "¿Desea importar el token?"
},
@ -1333,9 +1327,6 @@
"message": "verifique los detalles de la red",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"missingNFT": {
"message": "¿No ve su NFT?"
},
"mustSelectOne": {
"message": "Debe seleccionar al menos 1 token."
},
@ -1468,9 +1459,6 @@
"noConversionRateAvailable": {
"message": "No hay tasa de conversión disponible"
},
"noNFTs": {
"message": "Aún no hay NFT"
},
"noTransactions": {
"message": "No tiene transacciones"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Kustuta"
},
"deleteAccount": {
"message": "Kustuta konto"
},
"deleteNetwork": {
"message": "Võrk kustutada?"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "حذف"
},
"deleteAccount": {
"message": "حذف حساب"
},
"deleteNetwork": {
"message": "شبکه حذف شود؟"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Poista"
},
"deleteAccount": {
"message": "Poista tili"
},
"deleteNetwork": {
"message": "Poistetaanko verkko?"
},

View File

@ -187,9 +187,6 @@
"delete": {
"message": "I-delete"
},
"deleteAccount": {
"message": "I-delete ang Account"
},
"deleteNetwork": {
"message": "I-delete ang Network?"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "Supprimer"
},
"deleteAccount": {
"message": "Supprimer le compte"
},
"deleteNetwork": {
"message": "Supprimer le réseau ?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "LID dun NFT est un identifiant unique puisquil ny a pas deux NFT identiques. Encore une fois, sur OpenSea, ce numéro se trouve dans la section « Détails ». Prenez-en note ou copiez-le dans votre presse-papiers."
},
"importNFTs": {
"message": "Importer des NFT"
},
"importSelectedTokens": {
"message": "Voulez-vous importer les jetons sélectionnés ?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "Selon nos informations, la valeur de lURL RPC soumise ne correspond pas à un fournisseur connu pour cet ID de chaîne."
},
"missingNFT": {
"message": "Vous ne voyez pas votre NFT?"
},
"missingSetting": {
"message": "Vous ne trouvez pas un paramètre ?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "Aucun taux de conversion disponible"
},
"noNFTs": {
"message": "Aucun NFT pour le moment"
},
"noSnaps": {
"message": "Aucun Snap installé"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "מחיקה"
},
"deleteAccount": {
"message": "מחק חשבון"
},
"deleteNetwork": {
"message": "למחוק את הרשת?"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "हटाएँ"
},
"deleteAccount": {
"message": "अकाउंट हटाएं"
},
"deleteNetwork": {
"message": "नेटवर्क हटाएं?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "किसी एनएफटी की आईडी एक विशिष्ट पहचानकर्ता है क्योंकि कोई भी दो एनएफटी एक जैसे नहीं होते हैं। फिर से, OpenSea पर यह संख्या 'डिटेल्स' के नीचे होगी। इसे नोट कर लें या अपने क्लिपबोर्ड पर कॉपी कर लें।"
},
"importNFTs": {
"message": "NFT आयात करें"
},
"importSelectedTokens": {
"message": "चयनित टोकन आयात करें?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "हमारे रिकॉर्ड के अनुसार, सबमिट किया गया RPC-URL मान इस चेन आईडी के लिए किसी ज्ञात प्रदाता से मेल नहीं खाता।"
},
"missingNFT": {
"message": "अपना NFT नहीं देख रहे हैं?"
},
"missingSetting": {
"message": "सेटिंग नहीं मिल पाया?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "कोई भी रूपांतरण दर उपलब्ध नहीं है"
},
"noNFTs": {
"message": "अभी तक कोई NFT नहीं"
},
"noSnaps": {
"message": "कोई स्नैप इंस्टाल नहीं किया गया"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Izbriši"
},
"deleteAccount": {
"message": "Izbriši račun"
},
"deleteNetwork": {
"message": "Izbrisati mrežu?"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Törlés"
},
"deleteAccount": {
"message": "Fiók törlése"
},
"deleteNetwork": {
"message": "Törli a hálózatot?"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "Hapus"
},
"deleteAccount": {
"message": "Hapus akun"
},
"deleteNetwork": {
"message": "Hapus jaringan?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "ID NFT merupakan pengenal unik karena tidak ada dua NFT yang sama. Sekali lagi, angka ini berada di bawah 'Detail' pada OpenSea. Catat atau salin ke papan klip."
},
"importNFTs": {
"message": "Impor NFT"
},
"importSelectedTokens": {
"message": "Impor token yang dipilih?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "Menurut catatan kami, nilai URL RPC yang dikirimkan tidak sesuai dengan penyedia yang dikenal untuk ID rantai ini."
},
"missingNFT": {
"message": "Tidak melihat NFT Anda?"
},
"missingSetting": {
"message": "Tidak dapat menemukan pengaturan?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "Nilai konversi tidak tersedia"
},
"noNFTs": {
"message": "Belum ada NFT"
},
"noSnaps": {
"message": "Belum ada Snap yang diinstal"
},

View File

@ -716,9 +716,6 @@
"delete": {
"message": "Elimina"
},
"deleteAccount": {
"message": "Cancella account"
},
"deleteNetwork": {
"message": "Cancella la rete?"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "削除"
},
"deleteAccount": {
"message": "アカウントを削除"
},
"deleteNetwork": {
"message": "ネットワークを削除しますか?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "NFT の ID は一意の識別子で、同じ NFT は 2 つとして存在しません。前述の通り、OpenSea ではこの番号は「詳細」に表示されます。この ID を書き留めるか、クリップボードにコピーしてください。"
},
"importNFTs": {
"message": "NFTをインポート"
},
"importSelectedTokens": {
"message": "選択したトークンをインポートしますか?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "弊社の記録によると、送信された RPC URL の値がこのチェーン ID の既知のプロバイダーと一致しません。"
},
"missingNFT": {
"message": "NFTが見当たりませんか"
},
"missingSetting": {
"message": "設定が見つかりませんか?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "利用可能な換算レートがありません"
},
"noNFTs": {
"message": "NFTはまだありません"
},
"noSnaps": {
"message": "スナップがインストールされていません"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "ಅಳಿಸಿ"
},
"deleteAccount": {
"message": "ಖಾತೆಯನ್ನು ಅಳಿಸಿ"
},
"deleteNetwork": {
"message": "ನೆಟ್‌ವರ್ಕ್ ಅಳಿಸುವುದೇ?"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "삭제"
},
"deleteAccount": {
"message": "계정 삭제"
},
"deleteNetwork": {
"message": "네트워크를 삭제할까요?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "NFT의 ID는 고유한 식별자이므로 동일한 NFT는 존재하지 않습니다. 다시 말하지만, OpenSea에서 이 번호는 '세부 정보(Details)'에서 찾아볼 수 있습니다. 이를 기록하거나 클립보드에 복사해 두세요."
},
"importNFTs": {
"message": "NFT 가져오기"
},
"importSelectedTokens": {
"message": "선택한 토큰을 불러올까요?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "기록에 따르면 제출한 RPC URL 값이 이 체인 ID의 알려진 공급업체와 일치하지 않습니다."
},
"missingNFT": {
"message": "NFT가 보이지 않나요?"
},
"missingSetting": {
"message": "설정을 찾으세요?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "사용 가능한 환율 없음"
},
"noNFTs": {
"message": "아직 NFT가 없음"
},
"noSnaps": {
"message": "설치된 스냅이 없습니다"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Ištrinti"
},
"deleteAccount": {
"message": "Šalinti paskyrą"
},
"deleteNetwork": {
"message": "Panaikinti tinklą?"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Dzēst"
},
"deleteAccount": {
"message": "Dzēst kontu"
},
"deleteNetwork": {
"message": "Dzēst tīklu?"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Padam"
},
"deleteAccount": {
"message": "Hapus Akaun"
},
"deleteNetwork": {
"message": "Padamkan Rangkaian?"
},

View File

@ -208,9 +208,6 @@
"delete": {
"message": "Slett"
},
"deleteAccount": {
"message": "Slett konto "
},
"deleteNetwork": {
"message": "Slette nettverk? "
},

View File

@ -406,9 +406,6 @@
"delete": {
"message": "I-delete"
},
"deleteAccount": {
"message": "I-delete ang Account"
},
"deleteNetwork": {
"message": "I-delete ang Network?"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Usuń"
},
"deleteAccount": {
"message": "Usuń konto"
},
"deleteNetwork": {
"message": "Usunąć sieć?"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "Excluir"
},
"deleteAccount": {
"message": "Excluir conta"
},
"deleteNetwork": {
"message": "Excluir rede?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "O ID de um NFT é um identificador único, pois não há dois NFTs iguais. Novamente, na OpenSea, esse número se encontra em \"Detalhes\". Anote-o ou copie-o para sua área de transferência."
},
"importNFTs": {
"message": "Importar NFTs"
},
"importSelectedTokens": {
"message": "Importar tokens selecionados?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "De acordo com os nossos registros, o valor da URL da RPC enviado não corresponde a um provedor conhecido da ID desta cadeia."
},
"missingNFT": {
"message": "Não está vendo o seu NFT?"
},
"missingSetting": {
"message": "Não consegue encontrar uma configuração?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "Não há uma taxa de conversão disponível"
},
"noNFTs": {
"message": "Nenhum NFT até agora"
},
"noSnaps": {
"message": "Nenhum snap instalado"
},

View File

@ -581,9 +581,6 @@
"delete": {
"message": "Excluir"
},
"deleteAccount": {
"message": "Excluir conta"
},
"deleteNetwork": {
"message": "Excluir rede?"
},
@ -1073,9 +1070,6 @@
"importMyWallet": {
"message": "Importar minha carteira"
},
"importNFTs": {
"message": "Importar NFTs"
},
"importTokenQuestion": {
"message": "Importar token?"
},
@ -1333,9 +1327,6 @@
"message": "verifique os detalhes da rede",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"missingNFT": {
"message": "Não está vendo o seu NFT?"
},
"mustSelectOne": {
"message": "Selecione pelo menos 1 token."
},
@ -1468,9 +1459,6 @@
"noConversionRateAvailable": {
"message": "Não há uma taxa de conversão disponível"
},
"noNFTs": {
"message": "Ainda não há nenhum NFT"
},
"noTransactions": {
"message": "Você não tem transações"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Șterge"
},
"deleteAccount": {
"message": "Ștergeți cont"
},
"deleteNetwork": {
"message": "Ștergeți rețeaua?"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "Удалить"
},
"deleteAccount": {
"message": "Удалить счет"
},
"deleteNetwork": {
"message": "Удалить сеть?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "Идентификатор коллекционного актива является уникальным идентификатором, поскольку нет двух одинаковых NFT. Опять же, в OpenSea этот номер находится в разделе «Подробности». Запишите его или скопируйте в буфер обмена."
},
"importNFTs": {
"message": "Импорт NFT"
},
"importSelectedTokens": {
"message": "Импортировать выбранные токены?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "Согласно нашим записям, отправленное значение URL-адреса RPC не соответствует известному поставщику для этого идентификатора блокчейна."
},
"missingNFT": {
"message": "Не видите свои NFT?"
},
"missingSetting": {
"message": "Не удается найти настройку?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "Нет доступного обменного курса"
},
"noNFTs": {
"message": "Пока нет NFT-токенов"
},
"noSnaps": {
"message": "Снапы не установлены"
},

View File

@ -205,9 +205,6 @@
"delete": {
"message": "Odstrániť"
},
"deleteAccount": {
"message": "Zmazať účet"
},
"deleteNetwork": {
"message": "Odstrániť sieť?"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Izbriši"
},
"deleteAccount": {
"message": "Izbriši račun"
},
"deleteNetwork": {
"message": "Izbrišem to omrežje?"
},

View File

@ -208,9 +208,6 @@
"delete": {
"message": "Избриши"
},
"deleteAccount": {
"message": "Obriši nalog"
},
"deleteNetwork": {
"message": "Da li želite da obrišete mrežu?"
},

View File

@ -205,9 +205,6 @@
"delete": {
"message": "Radera"
},
"deleteAccount": {
"message": "Radera konto"
},
"deleteNetwork": {
"message": "Radera nätverk?"
},

View File

@ -205,9 +205,6 @@
"delete": {
"message": "Futa"
},
"deleteAccount": {
"message": "Futa Akaunti"
},
"deleteNetwork": {
"message": "Futa Mtandao?"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "I-delete"
},
"deleteAccount": {
"message": "I-delete ang Account"
},
"deleteNetwork": {
"message": "I-delete ang network?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "Ang ID ng NFT ay isang natatanging pagkakakilanlan dahil walang dalawang NFT ang magkatulad. Muli, sa OpenSea ang numerong ito ay nasa ilalim ng 'Mga Detalye'. Itala ito, o kopyahin ito sa iyong clipboard."
},
"importNFTs": {
"message": "I-import ang mga NFT"
},
"importSelectedTokens": {
"message": "I-import ang mga napiling token?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "Ayon sa aming mga talaan, ang isinumiteng RPC URL value ay hindi tumutugma sa isang kilalang provider para sa chain ID na ito."
},
"missingNFT": {
"message": "Hindi makita ang NFT mo?"
},
"missingSetting": {
"message": "Hindi makahanap ng setting?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "Hindi available ang rate ng conversion"
},
"noNFTs": {
"message": "Wala pang mga NFT"
},
"noSnaps": {
"message": "Walang mga Snap na naka-install"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "Sil"
},
"deleteAccount": {
"message": "Hesabı Sil"
},
"deleteNetwork": {
"message": "Ağı Sil?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "Hiçbir iki NFT kimliği birbiriyle aynı olmadığı için her NFT kimliği benzersiz bir tanımlayıcıdır. Yine, OpenSea'de bu sayı 'Detaylar' kısmının altında yer alır. Not alın veya panonuza kopyalayın."
},
"importNFTs": {
"message": "NFS'leri İçe Aktar"
},
"importSelectedTokens": {
"message": "Seçilen tokenleri içe aktar?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "Kayıtlarımıza göre, sunulan RPC URL adresi değeri bu zincir kimliğinin bilinen bir sağlayıcısı ile uyumlu değil."
},
"missingNFT": {
"message": "NFT'nizi görmüyor musunuz?"
},
"missingSetting": {
"message": "Bir ayarı bulamıyor musun?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "Dönüşüm oranı mevcut değil"
},
"noNFTs": {
"message": "Henüz NFT yok"
},
"noSnaps": {
"message": "Hiç Snap yüklü değil"
},

View File

@ -211,9 +211,6 @@
"delete": {
"message": "Видалити"
},
"deleteAccount": {
"message": "Видалити обліковий запис"
},
"deleteNetwork": {
"message": "Видалити мережу?"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "Xóa"
},
"deleteAccount": {
"message": "Xóa tài khoản"
},
"deleteNetwork": {
"message": "Xóa mạng?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "ID của NFT là một mã nhận dạng duy nhất vì không có hai NFT nào giống hệt nhau. Một lần nữa, trên OpenSea, mã số này nằm bên dưới mục 'Chi tiết'. Hãy ghi chú lại hoặc sao chép vào bộ nhớ đệm."
},
"importNFTs": {
"message": "Nhập NFT"
},
"importSelectedTokens": {
"message": "Nhập các token đã chọn?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "Theo hồ sơ của chúng tôi, giá trị RPC URL đã gửi không khớp với một nhà cung cấp đã biết cho ID chuỗi này."
},
"missingNFT": {
"message": "Không thấy NFT của mình?"
},
"missingSetting": {
"message": "Không tìm thấy thiết lập?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "Không có sẵn tỷ lệ quy đổi nào"
},
"noNFTs": {
"message": "Chưa có NFT"
},
"noSnaps": {
"message": "Chưa cài đặt Snap nào"
},

View File

@ -911,9 +911,6 @@
"delete": {
"message": "删除"
},
"deleteAccount": {
"message": "删除账户"
},
"deleteNetwork": {
"message": "删除网络?"
},
@ -1602,9 +1599,6 @@
"importNFTTokenIdToolTip": {
"message": "NFT的ID是唯一标识符因为所有NFT都是独一无二的。同样在OpenSea上此数字位于“详情”下方。记下它或将它复制到剪贴板上。"
},
"importNFTs": {
"message": "添加收藏品"
},
"importSelectedTokens": {
"message": "要导入所选代币吗?"
},
@ -1956,9 +1950,6 @@
"mismatchedRpcUrl": {
"message": "根据我们的记录所提交的RPC URL值与此链ID的已知提供者不匹配。"
},
"missingNFT": {
"message": "找不到您的 NFT"
},
"missingSetting": {
"message": "找不到设置吗?"
},
@ -2165,9 +2156,6 @@
"noConversionRateAvailable": {
"message": "无可用汇率"
},
"noNFTs": {
"message": "尚无 NFT"
},
"noSnaps": {
"message": "没有安装Snap"
},

View File

@ -408,9 +408,6 @@
"delete": {
"message": "刪除"
},
"deleteAccount": {
"message": "刪除帳戶"
},
"deleteNetwork": {
"message": "刪除網路?"
},

View File

@ -1,14 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_735_24127)">
<path
d="M3.99902 19C7.24796 17.5 8.87242 16.75 10.4969 16.75M16.9948 19C13.7458 17.5 12.1214 16.75 10.4969 16.75M10.4969 16.75V11.5M10.4969 11.5L10 10.4091M10.4969 11.5V9.5L10.9967 8.5M10 10.4091C10 10.4091 5.00889 11.0985 2.99935 9.5C1.29118 8.14126 1 4.5 1 4.5C1 4.5 5.55008 3.95155 7.54545 5.90909C8.91802 7.25563 10 10.4091 10 10.4091ZM10.9967 8.5C10.9967 8.5 11.5374 4.11404 13.4959 2.5C15.2137 1.08439 18.9941 1 18.9941 1C18.9941 1 19.1777 5.2683 17.4946 7C15.6792 8.86783 10.9967 8.5 10.9967 8.5Z"
strokeWidth="1.5"
strokeLinecap="round"
/>
</g>
<defs>
<clipPath id="clip0_735_24127">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.59902 17.1C6.52306 15.75 7.98508 15.075 9.44711 15.075M15.2952 17.1C12.3711 15.75 10.9091 15.075 9.44711 15.075M9.44711 15.075V10.35M9.44711 10.35L8.9999 9.36821M9.44711 10.35V8.55002L9.89696 7.65002M8.9999 9.36821C8.9999 9.36821 4.5079 9.98865 2.69931 8.55002C1.16197 7.32715 0.899902 4.05002 0.899902 4.05002C0.899902 4.05002 4.99497 3.55642 6.79081 5.31821C8.02612 6.53009 8.9999 9.36821 8.9999 9.36821ZM9.89696 7.65002C9.89696 7.65002 10.3835 3.70266 12.1462 2.25002C13.6922 0.975978 17.0946 0.900024 17.0946 0.900024C17.0946 0.900024 17.2598 4.74149 15.745 6.30002C14.1112 7.98107 9.89696 7.65002 9.89696 7.65002Z" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 873 B

After

Width:  |  Height:  |  Size: 794 B

View File

@ -485,9 +485,11 @@ const onMessageSetUpExtensionStreams = (msg) => {
/**
* This listener destroys the extension streams when the extension port is disconnected,
* so that streams may be re-established later when the extension port is reconnected.
*
* @param {Error} [err] - Stream connection error
*/
const onDisconnectDestroyStreams = () => {
const err = checkForLastError();
const onDisconnectDestroyStreams = (err) => {
const lastErr = err || checkForLastError();
extensionPort.onDisconnect.removeListener(onDisconnectDestroyStreams);
@ -501,8 +503,8 @@ const onDisconnectDestroyStreams = () => {
* may cause issues. We suspect that this is a chromium bug as this event should only be called
* once the port and connections are ready. Delay time is arbitrary.
*/
if (err) {
console.warn(`${err} Resetting the streams.`);
if (lastErr) {
console.warn(`${lastErr} Resetting the streams.`);
setTimeout(setupExtensionStreams, 1000);
}
};
@ -630,6 +632,18 @@ const start = () => {
injectScript(inpageBundle);
}
initStreams();
// https://bugs.chromium.org/p/chromium/issues/detail?id=1457040
// Temporary workaround for chromium bug that breaks the content script <=> background connection
// for prerendered pages. This resets potentially broken extension streams if a page transitions
// from the prerendered state to the active state.
if (document.prerendering) {
document.addEventListener('prerenderingchange', () => {
onDisconnectDestroyStreams(
new Error('Prerendered page has become active.'),
);
});
}
}
};

View File

@ -1,6 +1,7 @@
import EventEmitter from 'events';
import log from 'loglevel';
import { captureException } from '@sentry/browser';
import { isEqual } from 'lodash';
import {
PersonalMessageManager,
TypedMessageManager,
@ -14,11 +15,17 @@ import {
REFRESH_TOKEN_CHANGE_EVENT,
INTERACTIVE_REPLACEMENT_TOKEN_CHANGE_EVENT,
} from '@metamask-institutional/sdk';
import {
handleMmiPortfolio,
setDashboardCookie,
} from '@metamask-institutional/portfolio-dashboard';
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
import { CHAIN_IDS } from '../../../shared/constants/network';
import {
BUILD_QUOTE_ROUTE,
CONNECT_HARDWARE_ROUTE,
} from '../../../ui/helpers/constants/routes';
import { previousValueComparator } from '../lib/util';
import { getPermissionBackgroundApiMethods } from './permissions';
export default class MMIController extends EventEmitter {
@ -69,6 +76,17 @@ export default class MMIController extends EventEmitter {
this.transactionUpdateController.subscribeToEvents();
});
}
this.preferencesController.store.subscribe(
previousValueComparator(async (prevState, currState) => {
const { identities: prevIdentities } = prevState;
const { identities: currIdentities } = currState;
if (isEqual(prevIdentities, currIdentities)) {
return;
}
await this.prepareMmiPortfolio();
}, this.preferencesController.store.getState()),
);
} // End of constructor
async persistKeyringsAfterRefreshTokenChange() {
@ -542,6 +560,44 @@ export default class MMIController extends EventEmitter {
});
}
async handleMmiDashboardData() {
await this.appStateController.getUnlockPromise(true);
const keyringAccounts = await this.keyringController.getAccounts();
const { identities } = this.preferencesController.store.getState();
const { metaMetricsId } = this.metaMetricsController.store.getState();
const getAccountDetails = (address) =>
this.custodyController.getAccountDetails(address);
const extensionId = this.extension.runtime.id;
const networks = [
...this.preferencesController.getRpcMethodPreferences(),
{ chainId: CHAIN_IDS.MAINNET },
{ chainId: CHAIN_IDS.GOERLI },
];
return handleMmiPortfolio({
keyringAccounts,
identities,
metaMetricsId,
networks,
getAccountDetails,
extensionId,
});
}
async prepareMmiPortfolio() {
if (!process.env.IN_TEST) {
try {
const mmiDashboardData = await this.handleMmiDashboardData();
const cookieSetUrls =
this.mmiConfigurationController.store.mmiConfiguration?.portfolio
?.cookieSetUrls;
setDashboardCookie(mmiDashboardData, cookieSetUrls);
} catch (error) {
console.error(error);
}
}
}
async setAccountAndNetwork(origin, address, chainId) {
await this.appStateController.getUnlockPromise(true);
const selectedAddress = this.preferencesController.getSelectedAddress();

View File

@ -8,14 +8,6 @@ import {
buildSnapRestrictedMethodSpecifications,
} from './snap-permissions';
// Temporarily replace the snaps packages with the Flask versions.
jest.mock('@metamask/snaps-controllers', () =>
jest.requireActual('@metamask/snaps-controllers-flask'),
);
jest.mock('@metamask/rpc-methods', () =>
jest.requireActual('@metamask/rpc-methods-flask'),
);
describe('buildSnapRestrictedMethodSpecifications', () => {
it('creates valid permission specification objects', () => {
const hooks = {

View File

@ -1,8 +1,5 @@
import { ObservableStore } from '@metamask/obs-store';
import { normalize as normalizeAddress } from 'eth-sig-util';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { setDashboardCookie } from '@metamask-institutional/portfolio-dashboard';
///: END:ONLY_INCLUDE_IN
import { IPFS_DEFAULT_GATEWAY_URL } from '../../../shared/constants/network';
import { LedgerTransportTypes } from '../../../shared/constants/hardware-wallets';
import { ThemeType } from '../../../shared/constants/preferences';
@ -81,14 +78,6 @@ export default class PreferencesController {
this.store.setMaxListeners(13);
this.tokenListController = opts.tokenListController;
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.handleMmiDashboardData = opts.handleMmiDashboardData;
if (!process.env.IN_TEST) {
this.mmiConfigurationStore = opts.mmiConfigurationStore.getState();
}
///: END:ONLY_INCLUDE_IN
this._subscribeToInfuraAvailability();
global.setPreference = (key, value) => {
@ -261,10 +250,6 @@ export default class PreferencesController {
return ids;
}, {});
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.prepareMmiPortfolio();
///: END:ONLY_INCLUDE_IN
this.store.updateState({ identities });
}
@ -290,10 +275,6 @@ export default class PreferencesController {
this.setSelectedAddress(selected);
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.prepareMmiPortfolio();
///: END:ONLY_INCLUDE_IN
return address;
}
@ -350,10 +331,6 @@ export default class PreferencesController {
this.store.updateState({ identities, lostIdentities });
this.addAddresses(addresses);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.prepareMmiPortfolio();
///: END:ONLY_INCLUDE_IN
// If the selected account is no longer valid,
// select an arbitrary other account:
let selected = this.getSelectedAddress();
@ -534,21 +511,6 @@ export default class PreferencesController {
return this.store.getState().disabledRpcMethodPreferences;
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
async prepareMmiPortfolio() {
if (!process.env.IN_TEST) {
try {
const mmiDashboardData = await this.handleMmiDashboardData();
const cookieSetUrls =
this.mmiConfigurationStore.mmiConfiguration?.portfolio?.cookieSetUrls;
setDashboardCookie(mmiDashboardData, cookieSetUrls);
} catch (error) {
console.error(error);
}
}
}
///: END:ONLY_INCLUDE_IN
//
// PRIVATE METHODS
//

View File

@ -6,6 +6,7 @@ if (
) {
console.log = noop;
console.info = noop;
console.warn = noop;
}
function noop() {

View File

@ -1,8 +1,10 @@
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { permittedMethods as permittedSnapMethods } from '@metamask/rpc-methods';
///: END:ONLY_INCLUDE_IN
import { permissionRpcMethods } from '@metamask/permission-controller';
import { selectHooks } from '@metamask/rpc-methods/dist/utils';
import {
selectHooks,
///: BEGIN:ONLY_INCLUDE_IN(snaps)
permittedMethods as permittedSnapMethods,
///: END:ONLY_INCLUDE_IN
} from '@metamask/rpc-methods';
import { ethErrors } from 'eth-rpc-errors';
import { flatten } from 'lodash';
import { UNSUPPORTED_RPC_METHODS } from '../../../../shared/constants/network';

View File

@ -1,3 +1,5 @@
import { ERC1155, ERC721 } from '@metamask/controller-utils';
import { ethErrors } from 'eth-rpc-errors';
import { MESSAGE_TYPE } from '../../../../../shared/constants/app';
const watchAsset = {
@ -39,6 +41,21 @@ async function watchAssetHandler(
params: { options: asset, type },
origin,
} = req;
const { tokenId } = asset;
if (
[ERC721, ERC1155].includes(type) &&
tokenId !== undefined &&
typeof tokenId !== 'string'
) {
return end(
ethErrors.rpc.invalidParams({
message: `Expected parameter 'tokenId' to be type 'string'. Received type '${typeof tokenId}'`,
}),
);
}
await handleWatchAssetRequest(asset, type, origin);
res.result = true;
return end();

View File

@ -0,0 +1,99 @@
import { ERC20, ERC721 } from '@metamask/controller-utils';
import { ethErrors } from 'eth-rpc-errors';
import watchAssetHandler from './watch-asset';
describe('watchAssetHandler', () => {
let mockEnd;
let mockHandleWatchAssetRequest;
beforeEach(() => {
mockEnd = jest.fn();
mockHandleWatchAssetRequest = jest.fn();
});
it('should handle valid input for type ERC721 correctly', async () => {
const req = {
params: {
options: {
address: '0x1234',
tokenId: 'testTokenId',
},
type: ERC721,
},
origin: 'testOrigin',
};
const res = {
result: false,
};
await watchAssetHandler.implementation(req, res, null, mockEnd, {
handleWatchAssetRequest: mockHandleWatchAssetRequest,
});
expect(mockHandleWatchAssetRequest).toHaveBeenCalledWith(
req.params.options,
req.params.type,
req.origin,
);
expect(res.result).toStrictEqual(true);
expect(mockEnd).toHaveBeenCalledWith();
});
it('should handle valid input for type ERC20 correctly', async () => {
const req = {
params: {
options: {
address: '0x1234',
symbol: 'TEST',
decimals: 18,
},
type: ERC20,
},
origin: 'testOrigin',
};
const res = {
result: false,
};
await watchAssetHandler.implementation(req, res, null, mockEnd, {
handleWatchAssetRequest: mockHandleWatchAssetRequest,
});
expect(mockHandleWatchAssetRequest).toHaveBeenCalledWith(
req.params.options,
req.params.type,
req.origin,
);
expect(res.result).toStrictEqual(true);
expect(mockEnd).toHaveBeenCalledWith();
});
it('should throw when type is ERC721 and tokenId type is invalid', async () => {
const req = {
params: {
options: {
address: '0x1234',
tokenId: 222,
},
type: ERC721,
},
origin: 'testOrigin',
};
const res = {
result: false,
};
await watchAssetHandler.implementation(req, res, null, mockEnd, {
handleWatchAssetRequest: mockHandleWatchAssetRequest,
});
expect(mockEnd).toHaveBeenCalledWith(
ethErrors.rpc.invalidParams({
message: `Expected parameter 'tokenId' to be type 'string'. Received type 'number'`,
}),
);
});
});

View File

@ -42,26 +42,11 @@ const createLoggerMiddlewareMock = () => (req, res, next) => {
next();
};
// Temporarily replace the snaps packages with the Flask versions.
const proxyPermissions = proxyquire('./controllers/permissions', {
'./snaps/snap-permissions': proxyquire(
'./controllers/permissions/snaps/snap-permissions',
{
// eslint-disable-next-line node/global-require
'@metamask/snaps-controllers': require('@metamask/snaps-controllers-flask'),
// eslint-disable-next-line node/global-require
'@metamask/rpc-methods': require('@metamask/rpc-methods-flask'),
},
),
});
const TEST_SEED =
'debris dizzy just program just float decrease vacant alarm reduce speak stadium';
const MetaMaskController = proxyquire('./metamask-controller', {
'./lib/createLoggerMiddleware': { default: createLoggerMiddlewareMock },
// Temporarily replace the snaps packages with the Flask versions.
'./controllers/permissions': proxyPermissions,
}).default;
describe('MetaMaskController', function () {

View File

@ -71,7 +71,6 @@ import {
} from '@metamask-institutional/custody-keyring';
import { InstitutionalFeaturesController } from '@metamask-institutional/institutional-features';
import { CustodyController } from '@metamask-institutional/custody-controller';
import { handleMmiPortfolio } from '@metamask-institutional/portfolio-dashboard';
import { TransactionUpdateController } from '@metamask-institutional/transaction-update';
///: END:ONLY_INCLUDE_IN
import { SignatureController } from '@metamask/signature-controller';
@ -121,6 +120,9 @@ import {
///: END:ONLY_INCLUDE_IN
} from '../../shared/constants/permissions';
import { UI_NOTIFICATIONS } from '../../shared/notifications';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { UI_INSTITUTIONAL_NOTIFICATIONS } from '../../shared/notifications/institutional';
///: END:ONLY_INCLUDE_IN
import { MILLISECOND, SECOND } from '../../shared/constants/time';
import {
ORIGIN_METAMASK,
@ -379,10 +381,6 @@ export default class MetamaskController extends EventEmitter {
),
tokenListController: this.tokenListController,
provider: this.provider,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
handleMmiDashboardData: this.handleMmiDashboardData.bind(this),
mmiConfigurationStore: this.mmiConfigurationController.store,
///: END:ONLY_INCLUDE_IN
});
this.preferencesController.store.subscribe(async ({ currentLocale }) => {
@ -568,12 +566,8 @@ export default class MetamaskController extends EventEmitter {
),
getCurrentAccountEIP1559Compatibility:
this.getCurrentAccountEIP1559Compatibility.bind(this),
legacyAPIEndpoint: `${gasApiBaseUrl}/networks/<chain_id>/gasPrices`,
EIP1559APIEndpoint: `${gasApiBaseUrl}/networks/<chain_id>/suggestedGasFees`,
getCurrentNetworkLegacyGasAPICompatibility: () => {
const { chainId } = this.networkController.state.providerConfig;
return process.env.IN_TEST || chainId === CHAIN_IDS.MAINNET;
},
getCurrentNetworkLegacyGasAPICompatibility: () => false,
getChainId: () => this.networkController.state.providerConfig.chainId,
});
@ -622,9 +616,16 @@ export default class MetamaskController extends EventEmitter {
const announcementMessenger = this.controllerMessenger.getRestricted({
name: 'AnnouncementController',
});
let allAnnouncements = UI_NOTIFICATIONS;
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
allAnnouncements = UI_INSTITUTIONAL_NOTIFICATIONS;
///: END:ONLY_INCLUDE_IN
this.announcementController = new AnnouncementController({
messenger: announcementMessenger,
allAnnouncements: UI_NOTIFICATIONS,
allAnnouncements,
state: initState.AnnouncementController,
});
@ -3912,7 +3913,8 @@ export default class MetamaskController extends EventEmitter {
),
handleMmiCheckIfTokenIsPresent:
this.mmiController.handleMmiCheckIfTokenIsPresent.bind(this),
handleMmiDashboardData: this.handleMmiDashboardData.bind(this),
handleMmiDashboardData:
this.mmiController.handleMmiDashboardData.bind(this),
handleMmiOpenSwaps: this.mmiController.handleMmiOpenSwaps.bind(this),
handleMmiSetAccountAndNetwork:
this.mmiController.setAccountAndNetwork.bind(this),
@ -3971,37 +3973,6 @@ export default class MetamaskController extends EventEmitter {
return engine;
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
/**
* This method is needed in preferences controller
* so it needs to be here and not in our controller because
* preferences controllers is initiated first
*/
async handleMmiDashboardData() {
await this.appStateController.getUnlockPromise(true);
const keyringAccounts = await this.keyringController.getAccounts();
const { identities } = this.preferencesController.store.getState();
const { metaMetricsId } = this.metaMetricsController.store.getState();
const getAccountDetails = (address) =>
this.custodyController.getAccountDetails(address);
const extensionId = this.extension.runtime.id;
const networks = [
...this.preferencesController.getRpcMethodPreferences(),
{ chainId: CHAIN_IDS.MAINNET },
{ chainId: CHAIN_IDS.GOERLI },
];
return handleMmiPortfolio({
keyringAccounts,
identities,
metaMetricsId,
networks,
getAccountDetails,
extensionId,
});
}
///: END:ONLY_INCLUDE_IN
/**
* TODO:LegacyProvider: Delete
* A method for providing our public config info over a stream.

View File

@ -74,19 +74,6 @@ function MockEthContract() {
};
}
// Temporarily replace the snaps packages with the Flask versions.
const proxyPermissions = proxyquire('./controllers/permissions', {
'./snaps/snap-permissions': proxyquire(
'./controllers/permissions/snaps/snap-permissions',
{
// eslint-disable-next-line node/global-require
'@metamask/snaps-controllers': require('@metamask/snaps-controllers-flask'),
// eslint-disable-next-line node/global-require
'@metamask/rpc-methods': require('@metamask/rpc-methods-flask'),
},
),
});
// TODO, Feb 24, 2023:
// ethjs-contract is being added to proxyquire, but we might want to discontinue proxyquire
// this is for expediency as we resolve a bug for v10.26.0. The proper solution here would have
@ -95,14 +82,10 @@ const proxyPermissions = proxyquire('./controllers/permissions', {
const MetaMaskController = proxyquire('./metamask-controller', {
'./lib/createLoggerMiddleware': { default: createLoggerMiddlewareMock },
'ethjs-contract': MockEthContract,
// Temporarily replace the snaps packages with the Flask versions.
'./controllers/permissions': proxyPermissions,
}).default;
const MetaMaskControllerMV3 = proxyquire('./metamask-controller', {
'../../shared/modules/mv3.utils': { isManifestV3: true },
// Temporarily replace the snaps packages with the Flask versions.
'./controllers/permissions': proxyPermissions,
}).default;
const currentNetworkId = '5';

View File

@ -1,8 +1,21 @@
/*
NOTICE:
This Snow + LavaMoat scuttling integration is currently being used
with an experimental API (https://github.com/LavaMoat/LavaMoat/pull/462).
Changing this code must be done cautiously to avoid breaking the app!
*/
// eslint-disable-next-line import/unambiguous
(function () {
const log = console.log.bind(console);
const msg = 'SNOW INTERCEPTED NEW WINDOW CREATION IN METAMASK APP: ';
window.top.SNOW((win) => {
log(msg, win, win?.frameElement);
const msg =
'Snow detected a new realm creation attempt in MetaMask. Performing scuttling on new realm.';
Object.defineProperty(window.top, 'SCUTTLER', {
value: (realm, scuttle) => {
window.top.SNOW((win) => {
log(msg, win);
scuttle(win);
}, realm);
},
});
})();

View File

@ -51,7 +51,7 @@ buildTypes:
- SEGMENT_FLASK_WRITE_KEY
- ALLOW_LOCAL_SNAPS: true
- REQUIRE_SNAPS_ALLOWLIST: false
- IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/0.16.1-flask.1/index.html
- IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/0.35.2-flask.1/index.html
- SUPPORT_LINK: https://metamask-flask.zendesk.com/hc
- SUPPORT_REQUEST_LINK: https://metamask-flask.zendesk.com/hc/en-us/requests/new
- INFURA_ENV_KEY_REF: INFURA_FLASK_PROJECT_ID
@ -69,7 +69,7 @@ buildTypes:
- SEGMENT_FLASK_WRITE_KEY
- ALLOW_LOCAL_SNAPS: true
- REQUIRE_SNAPS_ALLOWLIST: false
- IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/0.16.1-flask.1/index.html
- IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/0.35.2-flask.1/index.html
- SUPPORT_LINK: https://metamask-flask.zendesk.com/hc
- SUPPORT_REQUEST_LINK: https://metamask-flask.zendesk.com/hc/en-us/requests/new
- INFURA_ENV_KEY_REF: INFURA_FLASK_PROJECT_ID

View File

@ -147,8 +147,11 @@ async function defineAndRunBuildTasks() {
// build lavamoat runtime file
await lavapack.buildRuntime({
scuttleGlobalThis: applyLavaMoat && shouldScuttle,
scuttleGlobalThisExceptions,
scuttleGlobalThis: {
enabled: applyLavaMoat && shouldScuttle,
scuttlerName: 'SCUTTLER',
exceptions: scuttleGlobalThisExceptions,
},
});
}

View File

@ -135,9 +135,12 @@ function createManifestTasks({
.trim()
.substring(0, 8);
manifest.name = `MetaMask ${capitalize(
buildType,
)}${mv3Str}${lavamoatStr}${snowStr}`;
const buildName =
buildType === 'mmi'
? `MetaMask Institutional ${mv3Str}${lavamoatStr}${snowStr}`
: `MetaMask ${capitalize(buildType)}${mv3Str}${lavamoatStr}${snowStr}`;
manifest.name = buildName;
manifest.description = `${environment} build from git id: ${gitRevisionStr}`;
}

View File

@ -186,8 +186,14 @@
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true,
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography>@noble/hashes": true,
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true
}
},
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": {
@ -214,7 +220,7 @@
"crypto": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": {
@ -222,7 +228,19 @@
"TextEncoder": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethersproject/abi": {
@ -1712,9 +1730,15 @@
},
"@metamask/rpc-methods": {
"packages": {
"@metamask/browser-passworder": true,
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/utils": true,
"@metamask/permission-controller": true,
"@metamask/rpc-methods>@metamask/utils": true,
"@metamask/rpc-methods>nanoid": true,
"@metamask/snaps-ui": true,
"@metamask/snaps-utils": true,
"eth-rpc-errors": true,
"superstruct": true
}
},
@ -1723,6 +1747,18 @@
"crypto.getRandomValues": true
}
},
"@metamask/rpc-methods>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"browserify>buffer": true,
"nock>debug": true,
"semver": true,
"superstruct": true
}
},
"@metamask/rpc-methods>nanoid": {
"globals": {
"crypto.getRandomValues": true
@ -1847,6 +1883,81 @@
"crypto.getRandomValues": true
}
},
"@metamask/snaps-ui": {
"packages": {
"@metamask/snaps-ui>@metamask/utils": true,
"superstruct": true
}
},
"@metamask/snaps-ui>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"browserify>buffer": true,
"nock>debug": true,
"semver": true,
"superstruct": true
}
},
"@metamask/snaps-utils": {
"globals": {
"TextDecoder": true,
"URL": true,
"console.error": true,
"console.log": true,
"console.warn": true,
"document.body.appendChild": true,
"document.createElement": true
},
"packages": {
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true,
"@metamask/snaps-utils>@metamask/utils": true,
"@metamask/snaps-utils>cron-parser": true,
"@metamask/snaps-utils>fast-json-stable-stringify": true,
"@metamask/snaps-utils>rfdc": true,
"@metamask/snaps-utils>validate-npm-package-name": true,
"semver": true,
"superstruct": true
}
},
"@metamask/snaps-utils>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"browserify>buffer": true,
"nock>debug": true,
"semver": true,
"superstruct": true
}
},
"@metamask/snaps-utils>cron-parser": {
"packages": {
"browserify>browser-resolve": true,
"luxon": true
}
},
"@metamask/snaps-utils>rfdc": {
"packages": {
"browserify>buffer": true
}
},
"@metamask/snaps-utils>validate-npm-package-name": {
"packages": {
"@metamask/snaps-utils>validate-npm-package-name>builtins": true
}
},
"@metamask/snaps-utils>validate-npm-package-name>builtins": {
"packages": {
"browserify>process": true,
"semver": true
}
},
"@metamask/subject-metadata-controller": {
"packages": {
"@metamask/subject-metadata-controller>@metamask/base-controller": true
@ -1948,85 +2059,110 @@
},
"@sentry/browser": {
"globals": {
"TextDecoder": true,
"TextEncoder": true,
"XMLHttpRequest": true,
"__SENTRY_DEBUG__": true,
"__SENTRY_RELEASE__": true,
"indexedDB.open": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry-internal/tracing": true,
"@sentry/browser>@sentry/core": true,
"@sentry/browser>@sentry/replay": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry-internal/tracing": {
"globals": {
"Headers": true,
"PerformanceObserver": true,
"Request": true,
"__SENTRY_DEBUG__": true,
"addEventListener": true,
"performance.getEntriesByType": true,
"removeEventListener": true
},
"packages": {
"@sentry/browser>@sentry/core": true,
"@sentry/browser>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core": {
"globals": {
"__SENTRY_DEBUG__": true,
"__SENTRY_TRACING__": true,
"clearInterval": true,
"setInterval": true
"clearTimeout": true,
"console.warn": true,
"setInterval": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub": true,
"@sentry/browser>@sentry/core>@sentry/minimal": true,
"@sentry/browser>@sentry/core>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core>@sentry/hub": {
"@sentry/browser>@sentry/replay": {
"globals": {
"clearInterval": true,
"setInterval": true
"Blob": true,
"CSSConditionRule": true,
"CSSGroupingRule": true,
"CSSMediaRule": true,
"CSSSupportsRule": true,
"DragEvent": true,
"Element": true,
"FormData": true,
"HTMLCanvasElement": true,
"HTMLElement.prototype": true,
"HTMLFormElement": true,
"HTMLImageElement": true,
"HTMLInputElement.prototype": true,
"HTMLOptionElement.prototype": true,
"HTMLSelectElement.prototype": true,
"HTMLTextAreaElement.prototype": true,
"Headers": true,
"ImageData": true,
"MouseEvent": true,
"MutationObserver": true,
"Node.prototype.contains": true,
"PerformanceObserver": true,
"TextEncoder": true,
"URL": true,
"URLSearchParams": true,
"Worker": true,
"Zone": true,
"__SENTRY_DEBUG__": true,
"__rrMutationObserver": true,
"clearTimeout": true,
"console.error": true,
"console.warn": true,
"document": true,
"innerHeight": true,
"innerWidth": true,
"location.href": true,
"pageXOffset": true,
"pageYOffset": true,
"requestAnimationFrame": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core>@sentry/hub>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>@sentry/core>@sentry/minimal": {
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub": true,
"@sentry/browser>@sentry/core>@sentry/minimal>tslib": true
}
},
"@sentry/browser>@sentry/core>@sentry/minimal>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>@sentry/core>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>tslib": {
"globals": {
"define": true
"@sentry/browser>@sentry/core": true,
"@sentry/utils": true,
"browserify>process": true
}
},
"@sentry/integrations": {
"globals": {
"clearTimeout": true,
"console.error": true,
"console.log": true,
"setTimeout": true
"Request": true,
"__SENTRY_DEBUG__": true,
"console.log": true
},
"packages": {
"@sentry/integrations>tslib": true,
"@sentry/types": true,
"@sentry/utils": true,
"localforage": true
}
},
"@sentry/integrations>tslib": {
"globals": {
"define": true
}
},
"@sentry/utils": {
"globals": {
"CustomEvent": true,
@ -2038,22 +2174,22 @@
"Headers": true,
"Request": true,
"Response": true,
"TextEncoder": true,
"URL": true,
"XMLHttpRequest.prototype": true,
"__SENTRY_BROWSER_BUNDLE__": true,
"__SENTRY_DEBUG__": true,
"clearTimeout": true,
"console.error": true,
"document": true,
"setTimeout": true
"new": true,
"setTimeout": true,
"target": true
},
"packages": {
"@sentry/utils>tslib": true,
"browserify>process": true
}
},
"@sentry/utils>tslib": {
"globals": {
"define": true
}
},
"@truffle/codec": {
"packages": {
"@truffle/codec>@truffle/abi-utils": true,
@ -4490,6 +4626,12 @@
"string.prototype.matchall>regexp.prototype.flags>functions-have-names": true
}
},
"superstruct": {
"globals": {
"console.warn": true,
"define": true
}
},
"uuid": {
"globals": {
"crypto": true,

View File

@ -186,8 +186,14 @@
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true,
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography>@noble/hashes": true,
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true
}
},
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": {
@ -214,7 +220,7 @@
"crypto": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": {
@ -222,7 +228,19 @@
"TextEncoder": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethersproject/abi": {
@ -1883,6 +1901,7 @@
"document.createElement": true
},
"packages": {
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true,
"@metamask/rpc-methods-flask>@metamask/utils": true,
@ -2082,6 +2101,7 @@
"document.createElement": true
},
"packages": {
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true,
"@metamask/snaps-controllers-flask>@metamask/utils": true,
@ -2293,6 +2313,7 @@
"document.createElement": true
},
"packages": {
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true,
"@metamask/snaps-utils-flask>@metamask/utils": true,
@ -2474,85 +2495,110 @@
},
"@sentry/browser": {
"globals": {
"TextDecoder": true,
"TextEncoder": true,
"XMLHttpRequest": true,
"__SENTRY_DEBUG__": true,
"__SENTRY_RELEASE__": true,
"indexedDB.open": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry-internal/tracing": true,
"@sentry/browser>@sentry/core": true,
"@sentry/browser>@sentry/replay": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry-internal/tracing": {
"globals": {
"Headers": true,
"PerformanceObserver": true,
"Request": true,
"__SENTRY_DEBUG__": true,
"addEventListener": true,
"performance.getEntriesByType": true,
"removeEventListener": true
},
"packages": {
"@sentry/browser>@sentry/core": true,
"@sentry/browser>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core": {
"globals": {
"__SENTRY_DEBUG__": true,
"__SENTRY_TRACING__": true,
"clearInterval": true,
"setInterval": true
"clearTimeout": true,
"console.warn": true,
"setInterval": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub": true,
"@sentry/browser>@sentry/core>@sentry/minimal": true,
"@sentry/browser>@sentry/core>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core>@sentry/hub": {
"@sentry/browser>@sentry/replay": {
"globals": {
"clearInterval": true,
"setInterval": true
"Blob": true,
"CSSConditionRule": true,
"CSSGroupingRule": true,
"CSSMediaRule": true,
"CSSSupportsRule": true,
"DragEvent": true,
"Element": true,
"FormData": true,
"HTMLCanvasElement": true,
"HTMLElement.prototype": true,
"HTMLFormElement": true,
"HTMLImageElement": true,
"HTMLInputElement.prototype": true,
"HTMLOptionElement.prototype": true,
"HTMLSelectElement.prototype": true,
"HTMLTextAreaElement.prototype": true,
"Headers": true,
"ImageData": true,
"MouseEvent": true,
"MutationObserver": true,
"Node.prototype.contains": true,
"PerformanceObserver": true,
"TextEncoder": true,
"URL": true,
"URLSearchParams": true,
"Worker": true,
"Zone": true,
"__SENTRY_DEBUG__": true,
"__rrMutationObserver": true,
"clearTimeout": true,
"console.error": true,
"console.warn": true,
"document": true,
"innerHeight": true,
"innerWidth": true,
"location.href": true,
"pageXOffset": true,
"pageYOffset": true,
"requestAnimationFrame": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core>@sentry/hub>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>@sentry/core>@sentry/minimal": {
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub": true,
"@sentry/browser>@sentry/core>@sentry/minimal>tslib": true
}
},
"@sentry/browser>@sentry/core>@sentry/minimal>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>@sentry/core>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>tslib": {
"globals": {
"define": true
"@sentry/browser>@sentry/core": true,
"@sentry/utils": true,
"browserify>process": true
}
},
"@sentry/integrations": {
"globals": {
"clearTimeout": true,
"console.error": true,
"console.log": true,
"setTimeout": true
"Request": true,
"__SENTRY_DEBUG__": true,
"console.log": true
},
"packages": {
"@sentry/integrations>tslib": true,
"@sentry/types": true,
"@sentry/utils": true,
"localforage": true
}
},
"@sentry/integrations>tslib": {
"globals": {
"define": true
}
},
"@sentry/utils": {
"globals": {
"CustomEvent": true,
@ -2564,22 +2610,22 @@
"Headers": true,
"Request": true,
"Response": true,
"TextEncoder": true,
"URL": true,
"XMLHttpRequest.prototype": true,
"__SENTRY_BROWSER_BUNDLE__": true,
"__SENTRY_DEBUG__": true,
"clearTimeout": true,
"console.error": true,
"document": true,
"setTimeout": true
"new": true,
"setTimeout": true,
"target": true
},
"packages": {
"@sentry/utils>tslib": true,
"browserify>process": true
}
},
"@sentry/utils>tslib": {
"globals": {
"define": true
}
},
"@truffle/codec": {
"packages": {
"@truffle/codec>@truffle/abi-utils": true,
@ -5148,6 +5194,12 @@
"string.prototype.matchall>regexp.prototype.flags>functions-have-names": true
}
},
"superstruct": {
"globals": {
"console.warn": true,
"define": true
}
},
"terser>source-map-support>buffer-from": {
"packages": {
"browserify>buffer": true

View File

@ -186,8 +186,14 @@
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true,
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography>@noble/hashes": true,
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true
}
},
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": {
@ -214,7 +220,7 @@
"crypto": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": {
@ -222,7 +228,19 @@
"TextEncoder": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethersproject/abi": {
@ -1883,6 +1901,7 @@
"document.createElement": true
},
"packages": {
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true,
"@metamask/rpc-methods-flask>@metamask/utils": true,
@ -2082,6 +2101,7 @@
"document.createElement": true
},
"packages": {
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true,
"@metamask/snaps-controllers-flask>@metamask/utils": true,
@ -2293,6 +2313,7 @@
"document.createElement": true
},
"packages": {
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true,
"@metamask/snaps-utils-flask>@metamask/utils": true,
@ -2474,85 +2495,110 @@
},
"@sentry/browser": {
"globals": {
"TextDecoder": true,
"TextEncoder": true,
"XMLHttpRequest": true,
"__SENTRY_DEBUG__": true,
"__SENTRY_RELEASE__": true,
"indexedDB.open": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry-internal/tracing": true,
"@sentry/browser>@sentry/core": true,
"@sentry/browser>@sentry/replay": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry-internal/tracing": {
"globals": {
"Headers": true,
"PerformanceObserver": true,
"Request": true,
"__SENTRY_DEBUG__": true,
"addEventListener": true,
"performance.getEntriesByType": true,
"removeEventListener": true
},
"packages": {
"@sentry/browser>@sentry/core": true,
"@sentry/browser>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core": {
"globals": {
"__SENTRY_DEBUG__": true,
"__SENTRY_TRACING__": true,
"clearInterval": true,
"setInterval": true
"clearTimeout": true,
"console.warn": true,
"setInterval": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub": true,
"@sentry/browser>@sentry/core>@sentry/minimal": true,
"@sentry/browser>@sentry/core>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core>@sentry/hub": {
"@sentry/browser>@sentry/replay": {
"globals": {
"clearInterval": true,
"setInterval": true
"Blob": true,
"CSSConditionRule": true,
"CSSGroupingRule": true,
"CSSMediaRule": true,
"CSSSupportsRule": true,
"DragEvent": true,
"Element": true,
"FormData": true,
"HTMLCanvasElement": true,
"HTMLElement.prototype": true,
"HTMLFormElement": true,
"HTMLImageElement": true,
"HTMLInputElement.prototype": true,
"HTMLOptionElement.prototype": true,
"HTMLSelectElement.prototype": true,
"HTMLTextAreaElement.prototype": true,
"Headers": true,
"ImageData": true,
"MouseEvent": true,
"MutationObserver": true,
"Node.prototype.contains": true,
"PerformanceObserver": true,
"TextEncoder": true,
"URL": true,
"URLSearchParams": true,
"Worker": true,
"Zone": true,
"__SENTRY_DEBUG__": true,
"__rrMutationObserver": true,
"clearTimeout": true,
"console.error": true,
"console.warn": true,
"document": true,
"innerHeight": true,
"innerWidth": true,
"location.href": true,
"pageXOffset": true,
"pageYOffset": true,
"requestAnimationFrame": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core>@sentry/hub>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>@sentry/core>@sentry/minimal": {
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub": true,
"@sentry/browser>@sentry/core>@sentry/minimal>tslib": true
}
},
"@sentry/browser>@sentry/core>@sentry/minimal>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>@sentry/core>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>tslib": {
"globals": {
"define": true
"@sentry/browser>@sentry/core": true,
"@sentry/utils": true,
"browserify>process": true
}
},
"@sentry/integrations": {
"globals": {
"clearTimeout": true,
"console.error": true,
"console.log": true,
"setTimeout": true
"Request": true,
"__SENTRY_DEBUG__": true,
"console.log": true
},
"packages": {
"@sentry/integrations>tslib": true,
"@sentry/types": true,
"@sentry/utils": true,
"localforage": true
}
},
"@sentry/integrations>tslib": {
"globals": {
"define": true
}
},
"@sentry/utils": {
"globals": {
"CustomEvent": true,
@ -2564,22 +2610,22 @@
"Headers": true,
"Request": true,
"Response": true,
"TextEncoder": true,
"URL": true,
"XMLHttpRequest.prototype": true,
"__SENTRY_BROWSER_BUNDLE__": true,
"__SENTRY_DEBUG__": true,
"clearTimeout": true,
"console.error": true,
"document": true,
"setTimeout": true
"new": true,
"setTimeout": true,
"target": true
},
"packages": {
"@sentry/utils>tslib": true,
"browserify>process": true
}
},
"@sentry/utils>tslib": {
"globals": {
"define": true
}
},
"@truffle/codec": {
"packages": {
"@truffle/codec>@truffle/abi-utils": true,
@ -5148,6 +5194,12 @@
"string.prototype.matchall>regexp.prototype.flags>functions-have-names": true
}
},
"superstruct": {
"globals": {
"console.warn": true,
"define": true
}
},
"terser>source-map-support>buffer-from": {
"packages": {
"browserify>buffer": true

View File

@ -186,8 +186,14 @@
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true,
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography>@noble/hashes": true,
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true
}
},
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": {
@ -214,7 +220,7 @@
"crypto": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": {
@ -222,7 +228,19 @@
"TextEncoder": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethersproject/abi": {
@ -1712,9 +1730,15 @@
},
"@metamask/rpc-methods": {
"packages": {
"@metamask/browser-passworder": true,
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/utils": true,
"@metamask/permission-controller": true,
"@metamask/rpc-methods>@metamask/utils": true,
"@metamask/rpc-methods>nanoid": true,
"@metamask/snaps-ui": true,
"@metamask/snaps-utils": true,
"eth-rpc-errors": true,
"superstruct": true
}
},
@ -1723,6 +1747,18 @@
"crypto.getRandomValues": true
}
},
"@metamask/rpc-methods>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"browserify>buffer": true,
"nock>debug": true,
"semver": true,
"superstruct": true
}
},
"@metamask/rpc-methods>nanoid": {
"globals": {
"crypto.getRandomValues": true
@ -1847,6 +1883,81 @@
"crypto.getRandomValues": true
}
},
"@metamask/snaps-ui": {
"packages": {
"@metamask/snaps-ui>@metamask/utils": true,
"superstruct": true
}
},
"@metamask/snaps-ui>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"browserify>buffer": true,
"nock>debug": true,
"semver": true,
"superstruct": true
}
},
"@metamask/snaps-utils": {
"globals": {
"TextDecoder": true,
"URL": true,
"console.error": true,
"console.log": true,
"console.warn": true,
"document.body.appendChild": true,
"document.createElement": true
},
"packages": {
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true,
"@metamask/snaps-utils>@metamask/utils": true,
"@metamask/snaps-utils>cron-parser": true,
"@metamask/snaps-utils>fast-json-stable-stringify": true,
"@metamask/snaps-utils>rfdc": true,
"@metamask/snaps-utils>validate-npm-package-name": true,
"semver": true,
"superstruct": true
}
},
"@metamask/snaps-utils>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"browserify>buffer": true,
"nock>debug": true,
"semver": true,
"superstruct": true
}
},
"@metamask/snaps-utils>cron-parser": {
"packages": {
"browserify>browser-resolve": true,
"luxon": true
}
},
"@metamask/snaps-utils>rfdc": {
"packages": {
"browserify>buffer": true
}
},
"@metamask/snaps-utils>validate-npm-package-name": {
"packages": {
"@metamask/snaps-utils>validate-npm-package-name>builtins": true
}
},
"@metamask/snaps-utils>validate-npm-package-name>builtins": {
"packages": {
"browserify>process": true,
"semver": true
}
},
"@metamask/subject-metadata-controller": {
"packages": {
"@metamask/subject-metadata-controller>@metamask/base-controller": true
@ -1948,85 +2059,110 @@
},
"@sentry/browser": {
"globals": {
"TextDecoder": true,
"TextEncoder": true,
"XMLHttpRequest": true,
"__SENTRY_DEBUG__": true,
"__SENTRY_RELEASE__": true,
"indexedDB.open": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry-internal/tracing": true,
"@sentry/browser>@sentry/core": true,
"@sentry/browser>@sentry/replay": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry-internal/tracing": {
"globals": {
"Headers": true,
"PerformanceObserver": true,
"Request": true,
"__SENTRY_DEBUG__": true,
"addEventListener": true,
"performance.getEntriesByType": true,
"removeEventListener": true
},
"packages": {
"@sentry/browser>@sentry/core": true,
"@sentry/browser>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core": {
"globals": {
"__SENTRY_DEBUG__": true,
"__SENTRY_TRACING__": true,
"clearInterval": true,
"setInterval": true
"clearTimeout": true,
"console.warn": true,
"setInterval": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub": true,
"@sentry/browser>@sentry/core>@sentry/minimal": true,
"@sentry/browser>@sentry/core>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core>@sentry/hub": {
"@sentry/browser>@sentry/replay": {
"globals": {
"clearInterval": true,
"setInterval": true
"Blob": true,
"CSSConditionRule": true,
"CSSGroupingRule": true,
"CSSMediaRule": true,
"CSSSupportsRule": true,
"DragEvent": true,
"Element": true,
"FormData": true,
"HTMLCanvasElement": true,
"HTMLElement.prototype": true,
"HTMLFormElement": true,
"HTMLImageElement": true,
"HTMLInputElement.prototype": true,
"HTMLOptionElement.prototype": true,
"HTMLSelectElement.prototype": true,
"HTMLTextAreaElement.prototype": true,
"Headers": true,
"ImageData": true,
"MouseEvent": true,
"MutationObserver": true,
"Node.prototype.contains": true,
"PerformanceObserver": true,
"TextEncoder": true,
"URL": true,
"URLSearchParams": true,
"Worker": true,
"Zone": true,
"__SENTRY_DEBUG__": true,
"__rrMutationObserver": true,
"clearTimeout": true,
"console.error": true,
"console.warn": true,
"document": true,
"innerHeight": true,
"innerWidth": true,
"location.href": true,
"pageXOffset": true,
"pageYOffset": true,
"requestAnimationFrame": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core>@sentry/hub>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>@sentry/core>@sentry/minimal": {
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub": true,
"@sentry/browser>@sentry/core>@sentry/minimal>tslib": true
}
},
"@sentry/browser>@sentry/core>@sentry/minimal>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>@sentry/core>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>tslib": {
"globals": {
"define": true
"@sentry/browser>@sentry/core": true,
"@sentry/utils": true,
"browserify>process": true
}
},
"@sentry/integrations": {
"globals": {
"clearTimeout": true,
"console.error": true,
"console.log": true,
"setTimeout": true
"Request": true,
"__SENTRY_DEBUG__": true,
"console.log": true
},
"packages": {
"@sentry/integrations>tslib": true,
"@sentry/types": true,
"@sentry/utils": true,
"localforage": true
}
},
"@sentry/integrations>tslib": {
"globals": {
"define": true
}
},
"@sentry/utils": {
"globals": {
"CustomEvent": true,
@ -2038,22 +2174,22 @@
"Headers": true,
"Request": true,
"Response": true,
"TextEncoder": true,
"URL": true,
"XMLHttpRequest.prototype": true,
"__SENTRY_BROWSER_BUNDLE__": true,
"__SENTRY_DEBUG__": true,
"clearTimeout": true,
"console.error": true,
"document": true,
"setTimeout": true
"new": true,
"setTimeout": true,
"target": true
},
"packages": {
"@sentry/utils>tslib": true,
"browserify>process": true
}
},
"@sentry/utils>tslib": {
"globals": {
"define": true
}
},
"@truffle/codec": {
"packages": {
"@truffle/codec>@truffle/abi-utils": true,
@ -4490,6 +4626,12 @@
"string.prototype.matchall>regexp.prototype.flags>functions-have-names": true
}
},
"superstruct": {
"globals": {
"console.warn": true,
"define": true
}
},
"uuid": {
"globals": {
"crypto": true,

View File

@ -186,8 +186,14 @@
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true,
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography>@noble/hashes": true,
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true
}
},
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": {
@ -214,7 +220,7 @@
"crypto": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": {
@ -222,7 +228,19 @@
"TextEncoder": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true
"@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethersproject/abi": {
@ -1933,9 +1951,15 @@
},
"@metamask/rpc-methods": {
"packages": {
"@metamask/browser-passworder": true,
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/utils": true,
"@metamask/permission-controller": true,
"@metamask/rpc-methods>@metamask/utils": true,
"@metamask/rpc-methods>nanoid": true,
"@metamask/snaps-ui": true,
"@metamask/snaps-utils": true,
"eth-rpc-errors": true,
"superstruct": true
}
},
@ -1944,6 +1968,18 @@
"crypto.getRandomValues": true
}
},
"@metamask/rpc-methods>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"browserify>buffer": true,
"nock>debug": true,
"semver": true,
"superstruct": true
}
},
"@metamask/rpc-methods>nanoid": {
"globals": {
"crypto.getRandomValues": true
@ -2068,6 +2104,81 @@
"crypto.getRandomValues": true
}
},
"@metamask/snaps-ui": {
"packages": {
"@metamask/snaps-ui>@metamask/utils": true,
"superstruct": true
}
},
"@metamask/snaps-ui>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"browserify>buffer": true,
"nock>debug": true,
"semver": true,
"superstruct": true
}
},
"@metamask/snaps-utils": {
"globals": {
"TextDecoder": true,
"URL": true,
"console.error": true,
"console.log": true,
"console.warn": true,
"document.body.appendChild": true,
"document.createElement": true
},
"packages": {
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true,
"@metamask/snaps-utils>@metamask/utils": true,
"@metamask/snaps-utils>cron-parser": true,
"@metamask/snaps-utils>fast-json-stable-stringify": true,
"@metamask/snaps-utils>rfdc": true,
"@metamask/snaps-utils>validate-npm-package-name": true,
"semver": true,
"superstruct": true
}
},
"@metamask/snaps-utils>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"browserify>buffer": true,
"nock>debug": true,
"semver": true,
"superstruct": true
}
},
"@metamask/snaps-utils>cron-parser": {
"packages": {
"browserify>browser-resolve": true,
"luxon": true
}
},
"@metamask/snaps-utils>rfdc": {
"packages": {
"browserify>buffer": true
}
},
"@metamask/snaps-utils>validate-npm-package-name": {
"packages": {
"@metamask/snaps-utils>validate-npm-package-name>builtins": true
}
},
"@metamask/snaps-utils>validate-npm-package-name>builtins": {
"packages": {
"browserify>process": true,
"semver": true
}
},
"@metamask/subject-metadata-controller": {
"packages": {
"@metamask/subject-metadata-controller>@metamask/base-controller": true
@ -2169,85 +2280,110 @@
},
"@sentry/browser": {
"globals": {
"TextDecoder": true,
"TextEncoder": true,
"XMLHttpRequest": true,
"__SENTRY_DEBUG__": true,
"__SENTRY_RELEASE__": true,
"indexedDB.open": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry-internal/tracing": true,
"@sentry/browser>@sentry/core": true,
"@sentry/browser>@sentry/replay": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry-internal/tracing": {
"globals": {
"Headers": true,
"PerformanceObserver": true,
"Request": true,
"__SENTRY_DEBUG__": true,
"addEventListener": true,
"performance.getEntriesByType": true,
"removeEventListener": true
},
"packages": {
"@sentry/browser>@sentry/core": true,
"@sentry/browser>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core": {
"globals": {
"__SENTRY_DEBUG__": true,
"__SENTRY_TRACING__": true,
"clearInterval": true,
"setInterval": true
"clearTimeout": true,
"console.warn": true,
"setInterval": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub": true,
"@sentry/browser>@sentry/core>@sentry/minimal": true,
"@sentry/browser>@sentry/core>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core>@sentry/hub": {
"@sentry/browser>@sentry/replay": {
"globals": {
"clearInterval": true,
"setInterval": true
"Blob": true,
"CSSConditionRule": true,
"CSSGroupingRule": true,
"CSSMediaRule": true,
"CSSSupportsRule": true,
"DragEvent": true,
"Element": true,
"FormData": true,
"HTMLCanvasElement": true,
"HTMLElement.prototype": true,
"HTMLFormElement": true,
"HTMLImageElement": true,
"HTMLInputElement.prototype": true,
"HTMLOptionElement.prototype": true,
"HTMLSelectElement.prototype": true,
"HTMLTextAreaElement.prototype": true,
"Headers": true,
"ImageData": true,
"MouseEvent": true,
"MutationObserver": true,
"Node.prototype.contains": true,
"PerformanceObserver": true,
"TextEncoder": true,
"URL": true,
"URLSearchParams": true,
"Worker": true,
"Zone": true,
"__SENTRY_DEBUG__": true,
"__rrMutationObserver": true,
"clearTimeout": true,
"console.error": true,
"console.warn": true,
"document": true,
"innerHeight": true,
"innerWidth": true,
"location.href": true,
"pageXOffset": true,
"pageYOffset": true,
"requestAnimationFrame": true,
"setTimeout": true
},
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub>tslib": true,
"@sentry/types": true,
"@sentry/utils": true
}
},
"@sentry/browser>@sentry/core>@sentry/hub>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>@sentry/core>@sentry/minimal": {
"packages": {
"@sentry/browser>@sentry/core>@sentry/hub": true,
"@sentry/browser>@sentry/core>@sentry/minimal>tslib": true
}
},
"@sentry/browser>@sentry/core>@sentry/minimal>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>@sentry/core>tslib": {
"globals": {
"define": true
}
},
"@sentry/browser>tslib": {
"globals": {
"define": true
"@sentry/browser>@sentry/core": true,
"@sentry/utils": true,
"browserify>process": true
}
},
"@sentry/integrations": {
"globals": {
"clearTimeout": true,
"console.error": true,
"console.log": true,
"setTimeout": true
"Request": true,
"__SENTRY_DEBUG__": true,
"console.log": true
},
"packages": {
"@sentry/integrations>tslib": true,
"@sentry/types": true,
"@sentry/utils": true,
"localforage": true
}
},
"@sentry/integrations>tslib": {
"globals": {
"define": true
}
},
"@sentry/utils": {
"globals": {
"CustomEvent": true,
@ -2259,22 +2395,22 @@
"Headers": true,
"Request": true,
"Response": true,
"TextEncoder": true,
"URL": true,
"XMLHttpRequest.prototype": true,
"__SENTRY_BROWSER_BUNDLE__": true,
"__SENTRY_DEBUG__": true,
"clearTimeout": true,
"console.error": true,
"document": true,
"setTimeout": true
"new": true,
"setTimeout": true,
"target": true
},
"packages": {
"@sentry/utils>tslib": true,
"browserify>process": true
}
},
"@sentry/utils>tslib": {
"globals": {
"define": true
}
},
"@truffle/codec": {
"packages": {
"@truffle/codec>@truffle/abi-utils": true,
@ -4711,6 +4847,12 @@
"string.prototype.matchall>regexp.prototype.flags>functions-have-names": true
}
},
"superstruct": {
"globals": {
"console.warn": true,
"define": true
}
},
"uuid": {
"globals": {
"crypto": true,

View File

@ -168,9 +168,13 @@
},
"@babel/eslint-parser": {
"builtin": {
"path": true
"module": true,
"path": true,
"worker_threads": true
},
"globals": {
"__dirname": true,
"process.cwd": true,
"process.versions": true
},
"packages": {
@ -980,7 +984,6 @@
"packages": {
"@lavamoat/allow-scripts>@npmcli/run-script>node-gyp>npmlog>are-we-there-yet": true,
"@lavamoat/allow-scripts>@npmcli/run-script>node-gyp>npmlog>gauge": true,
"@storybook/addon-mdx-gfm>@storybook/node-logger>npmlog>console-control-strings": true,
"@storybook/react>@storybook/node-logger>npmlog>console-control-strings": true,
"nyc>yargs>set-blocking": true
}
@ -1009,9 +1012,6 @@
"@lavamoat/allow-scripts>@npmcli/run-script>node-gyp>npmlog>gauge>aproba": true,
"@lavamoat/allow-scripts>@npmcli/run-script>node-gyp>npmlog>gauge>string-width": true,
"@lavamoat/allow-scripts>@npmcli/run-script>node-gyp>npmlog>gauge>strip-ansi": true,
"@storybook/addon-mdx-gfm>@storybook/node-logger>npmlog>console-control-strings": true,
"@storybook/addon-mdx-gfm>@storybook/node-logger>npmlog>gauge>has-unicode": true,
"@storybook/addon-mdx-gfm>@storybook/node-logger>npmlog>gauge>wide-align": true,
"@storybook/react>@storybook/node-logger>npmlog>console-control-strings": true,
"@storybook/react>@storybook/node-logger>npmlog>gauge>has-unicode": true,
"@storybook/react>@storybook/node-logger>npmlog>gauge>wide-align": true,
@ -1049,17 +1049,18 @@
"globals": {
"__dirname": true,
"__filename.slice": true,
"console.warn": true,
"process.cwd": true,
"setTimeout": true
},
"packages": {
"@lavamoat/lavapack>combine-source-map": true,
"@lavamoat/lavapack>lavamoat-core": true,
"@lavamoat/lavapack>convert-source-map": true,
"@lavamoat/lavapack>readable-stream": true,
"@lavamoat/lavapack>umd": true,
"browserify>JSONStream": true,
"lavamoat>json-stable-stringify": true,
"nyc>convert-source-map": true,
"lavamoat>lavamoat-core": true,
"through2": true
}
},
@ -1086,26 +1087,12 @@
"@lavamoat/lavapack>combine-source-map>inline-source-map>source-map": true
}
},
"@lavamoat/lavapack>lavamoat-core": {
"builtin": {
"events": true,
"fs.existsSync": true,
"fs.readFileSync": true,
"fs.writeFileSync": true,
"path.extname": true,
"path.join": true
},
"@lavamoat/lavapack>convert-source-map": {
"globals": {
"__dirname": true,
"console.error": true,
"console.warn": true,
"define": true
},
"packages": {
"lavamoat>json-stable-stringify": true,
"lavamoat>lavamoat-core>merge-deep": true,
"lavamoat>lavamoat-tofu": true,
"nyc>process-on-spawn>fromentries": true
"Buffer": true,
"atob": true,
"btoa": true,
"value": true
}
},
"@lavamoat/lavapack>readable-stream": {
@ -1149,21 +1136,6 @@
"string.prototype.matchall>side-channel": true
}
},
"@storybook/addon-mdx-gfm>@storybook/node-logger>npmlog>gauge>has-unicode": {
"builtin": {
"os.type": true
},
"globals": {
"process.env.LANG": true,
"process.env.LC_ALL": true,
"process.env.LC_CTYPE": true
}
},
"@storybook/addon-mdx-gfm>@storybook/node-logger>npmlog>gauge>wide-align": {
"packages": {
"yargs>string-width": true
}
},
"@storybook/core>@storybook/core-server>x-default-browser>default-browser-id>untildify>os-homedir": {
"builtin": {
"os.homedir": true
@ -2884,12 +2856,12 @@
"eslint-plugin-react>estraverse": true,
"eslint-plugin-react>jsx-ast-utils": true,
"eslint-plugin-react>object.entries": true,
"eslint-plugin-react>object.fromentries": true,
"eslint-plugin-react>object.hasown": true,
"eslint-plugin-react>object.values": true,
"eslint-plugin-react>resolve": true,
"eslint-plugin-react>semver": true,
"eslint>minimatch": true,
"lavamoat>object.fromentries": true,
"prop-types": true,
"string.prototype.matchall": true
}
@ -2949,6 +2921,13 @@
"string.prototype.matchall>es-abstract": true
}
},
"eslint-plugin-react>object.fromentries": {
"packages": {
"globalthis>define-properties": true,
"string.prototype.matchall>call-bind": true,
"string.prototype.matchall>es-abstract": true
}
},
"eslint-plugin-react>object.hasown": {
"packages": {
"string.prototype.matchall>es-abstract": true
@ -3010,6 +2989,9 @@
"util": true
},
"globals": {
"__filename": true,
"process.cwd": true,
"process.emitWarning": true,
"process.platform": true
},
"packages": {
@ -4895,20 +4877,9 @@
},
"packages": {
"@storybook/core>@storybook/core-server>x-default-browser>default-browser-id>untildify>os-homedir": true,
"gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-homedir": true,
"gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-tmpdir": true
}
},
"gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-homedir": {
"builtin": {
"os.homedir": true
},
"globals": {
"process.env": true,
"process.getuid": true,
"process.platform": true
}
},
"gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-tmpdir": {
"globals": {
"process.env.SystemRoot": true,
@ -4930,34 +4901,9 @@
"setTimeout": true
},
"packages": {
"gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob": true,
"nyc>glob": true
}
},
"gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob": {
"builtin": {
"assert": true,
"events.EventEmitter": true,
"fs": true,
"path.join": true,
"path.resolve": true,
"util": true
},
"globals": {
"console.error": true,
"process.cwd": true,
"process.nextTick": true,
"process.platform": true
},
"packages": {
"eslint>minimatch": true,
"gulp-watch>path-is-absolute": true,
"nyc>glob>fs.realpath": true,
"nyc>glob>inflight": true,
"pump>once": true,
"pumpify>inherits": true
}
},
"gulp-watch>chokidar>fsevents>node-pre-gyp>semver": {
"globals": {
"console": true,
@ -6203,8 +6149,8 @@
"setTimeout": true
},
"packages": {
"@lavamoat/lavapack": true,
"duplexify": true,
"lavamoat-browserify>@lavamoat/lavapack": true,
"lavamoat-browserify>browser-resolve": true,
"lavamoat-browserify>concat-stream": true,
"lavamoat-browserify>readable-stream": true,
@ -6214,37 +6160,6 @@
"lavamoat>lavamoat-core": true
}
},
"lavamoat-browserify>@lavamoat/lavapack": {
"builtin": {
"assert": true,
"buffer.Buffer.from": true,
"fs.promises.readFile": true,
"fs.promises.writeFile": true,
"fs.readFileSync": true,
"path.join": true,
"path.relative": true
},
"globals": {
"__dirname": true,
"process.cwd": true,
"setTimeout": true
},
"packages": {
"@lavamoat/lavapack>combine-source-map": true,
"@lavamoat/lavapack>umd": true,
"browserify>JSONStream": true,
"lavamoat-browserify>@lavamoat/lavapack>through2": true,
"lavamoat-browserify>readable-stream": true,
"lavamoat>json-stable-stringify": true,
"lavamoat>lavamoat-core": true,
"nyc>convert-source-map": true
}
},
"lavamoat-browserify>@lavamoat/lavapack>through2": {
"packages": {
"lavamoat-browserify>readable-stream": true
}
},
"lavamoat-browserify>browser-resolve": {
"builtin": {
"fs.readFile": true,
@ -6381,8 +6296,7 @@
"packages": {
"lavamoat>json-stable-stringify": true,
"lavamoat>lavamoat-core>merge-deep": true,
"lavamoat>lavamoat-tofu": true,
"nyc>process-on-spawn>fromentries": true
"lavamoat>lavamoat-tofu": true
}
},
"lavamoat>lavamoat-core>merge-deep": {
@ -6458,13 +6372,6 @@
"depcheck>@babel/traverse": true
}
},
"lavamoat>object.fromentries": {
"packages": {
"globalthis>define-properties": true,
"string.prototype.matchall>call-bind": true,
"string.prototype.matchall>es-abstract": true
}
},
"lodash": {
"globals": {
"define": true
@ -8678,6 +8585,12 @@
"jsdom>request>is-typedarray": true
}
},
"superstruct": {
"globals": {
"console.warn": true,
"define": true
}
},
"terser": {
"globals": {
"Buffer": true,
@ -9036,6 +8949,7 @@
},
"globals": {
"Error": true,
"__dirname": true,
"console": true,
"process": true
},
@ -9050,6 +8964,9 @@
}
},
"yargs>cliui": {
"globals": {
"process": true
},
"packages": {
"eslint>strip-ansi": true,
"yargs>cliui>wrap-ansi": true,

View File

@ -95,7 +95,6 @@
"fitness-functions": "ts-node development/fitness-functions/index.ts",
"generate-beta-commit": "node ./development/generate-beta-commit.js",
"validate-branch-name": "validate-branch-name",
"label-prs": "ts-node ./.github/scripts/label-prs.ts",
"add-release-label-to-pr-and-linked-issues": "ts-node ./.github/scripts/add-release-label-to-pr-and-linked-issues.ts"
},
"resolutions": {
@ -226,11 +225,11 @@
"@metamask/address-book-controller": "^3.0.0",
"@metamask/announcement-controller": "^4.0.0",
"@metamask/approval-controller": "^3.1.0",
"@metamask/assets-controllers": "^9.1.0",
"@metamask/assets-controllers": "^9.2.0",
"@metamask/base-controller": "^3.0.0",
"@metamask/browser-passworder": "^4.1.0",
"@metamask/contract-metadata": "^2.3.1",
"@metamask/controller-utils": "^4.0.0",
"@metamask/controller-utils": "^4.0.1",
"@metamask/design-tokens": "^1.9.0",
"@metamask/desktop": "^0.3.0",
"@metamask/eth-json-rpc-middleware": "^11.0.0",
@ -251,31 +250,31 @@
"@metamask/permission-controller": "^4.0.0",
"@metamask/phishing-controller": "^3.0.0",
"@metamask/post-message-stream": "^6.0.0",
"@metamask/providers": "^10.2.1",
"@metamask/providers": "^11.1.0",
"@metamask/rate-limit-controller": "^3.0.0",
"@metamask/rpc-methods": "^0.32.2",
"@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.34.0-flask.1",
"@metamask/rpc-methods": "^1.0.0-prerelease.1",
"@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.35.2-flask.1",
"@metamask/safe-event-emitter": "^2.0.0",
"@metamask/scure-bip39": "^2.0.3",
"@metamask/signature-controller": "^4.0.1",
"@metamask/slip44": "^3.0.0",
"@metamask/smart-transactions-controller": "^3.1.0",
"@metamask/snaps-controllers": "^0.32.2",
"@metamask/snaps-controllers-flask": "npm:@metamask/snaps-controllers@0.34.0-flask.1",
"@metamask/snaps-ui": "^0.32.2",
"@metamask/snaps-ui-flask": "npm:@metamask/snaps-ui@0.34.0-flask.1",
"@metamask/snaps-utils": "^0.32.2",
"@metamask/snaps-utils-flask": "npm:@metamask/snaps-utils@0.34.0-flask.1",
"@metamask/snaps-controllers": "^1.0.0-prerelease.1",
"@metamask/snaps-controllers-flask": "npm:@metamask/snaps-controllers@0.35.2-flask.1",
"@metamask/snaps-ui": "^1.0.0-prerelease.1",
"@metamask/snaps-ui-flask": "npm:@metamask/snaps-ui@0.35.2-flask.1",
"@metamask/snaps-utils": "^1.0.0-prerelease.1",
"@metamask/snaps-utils-flask": "npm:@metamask/snaps-utils@0.35.2-flask.1",
"@metamask/subject-metadata-controller": "^2.0.0",
"@metamask/utils": "^5.0.0",
"@ngraveio/bc-ur": "^1.1.6",
"@popperjs/core": "^2.4.0",
"@reduxjs/toolkit": "^1.6.2",
"@segment/loosely-validate-event": "^2.0.0",
"@sentry/browser": "^6.0.0",
"@sentry/integrations": "^6.0.0",
"@sentry/types": "^6.0.1",
"@sentry/utils": "^6.0.1",
"@sentry/browser": "^7.53.0",
"@sentry/integrations": "^7.53.0",
"@sentry/types": "^7.53.0",
"@sentry/utils": "^7.53.0",
"@truffle/codec": "^0.14.12",
"@truffle/decoder": "^5.3.5",
"@zxing/browser": "^0.0.10",
@ -371,7 +370,7 @@
"@babel/register": "^7.5.5",
"@ethersproject/bignumber": "^5.7.0",
"@lavamoat/allow-scripts": "^2.0.3",
"@lavamoat/lavapack": "^5.0.0",
"@lavamoat/lavapack": "^5.2.0",
"@metamask/auto-changelog": "^2.1.0",
"@metamask/eslint-config": "^9.0.0",
"@metamask/eslint-config-jest": "^9.0.0",
@ -380,7 +379,7 @@
"@metamask/eslint-config-typescript": "^9.0.1",
"@metamask/forwarder": "^1.1.0",
"@metamask/phishing-warning": "^2.1.0",
"@metamask/test-dapp": "^7.0.0",
"@metamask/test-dapp": "^7.0.1",
"@sentry/cli": "^1.58.0",
"@storybook/addon-a11y": "^7.0.11",
"@storybook/addon-actions": "^7.0.11",
@ -466,7 +465,7 @@
"fast-glob": "^3.2.2",
"fs-extra": "^8.1.0",
"ganache": "^v7.0.4",
"geckodriver": "^3.2.0",
"geckodriver": "^4.0.4",
"gh-pages": "^5.0.0",
"globby": "^11.0.4",
"gulp": "^4.0.2",
@ -494,8 +493,8 @@
"jsdom": "^11.2.0",
"junit-report-merger": "^4.0.0",
"koa": "^2.7.0",
"lavamoat": "^6.3.0",
"lavamoat-browserify": "^15.5.0",
"lavamoat": "^7.1.0",
"lavamoat-browserify": "^15.7.0",
"lavamoat-viz": "^6.0.9",
"lockfile-lint": "^4.9.6",
"loose-envify": "^1.4.0",

View File

@ -0,0 +1,13 @@
/**
* @typedef {object} SecurityProviderMessageSeverity
* @property {0} NOT_MALICIOUS - Indicates message is not malicious
* @property {1} MALICIOUS - Indicates message is malicious
* @property {2} NOT_SAFE - Indicates message is not safe
*/
/** @type {SecurityProviderMessageSeverity} */
export const SECURITY_PROVIDER_MESSAGE_SEVERITY = {
NOT_MALICIOUS: 0,
MALICIOUS: 1,
NOT_SAFE: 2,
};

View File

@ -12,14 +12,19 @@ export const EndowmentPermissions = Object.freeze({
// Methods / permissions in external packages that we are temporarily excluding.
export const ExcludedSnapPermissions = Object.freeze({
// TODO: Enable in Flask
///: BEGIN:ONLY_INCLUDE_IN(build-main,build-flask)
snap_manageAccounts:
'This permission is still in development and therefore not available.',
///: END:ONLY_INCLUDE_IN
eth_accounts:
'eth_accounts is disabled. For more information please see https://github.com/MetaMask/snaps-monorepo/issues/990.',
});
export const ExcludedSnapEndowments = Object.freeze({
///: BEGIN:ONLY_INCLUDE_IN(build-main)
'endowment:keyring':
'This endowment is still in development therefore not available.',
///: BEGIN:ONLY_INCLUDE_IN(build-main)
'endowment:long-running':
'endowment:long-running is deprecated. For more information please see https://github.com/MetaMask/snaps-monorepo/issues/945.',
///: END:ONLY_INCLUDE_IN

View File

@ -0,0 +1,30 @@
import { SECURITY_PROVIDER_MESSAGE_SEVERITY } from '../constants/security-provider';
import { isSuspiciousResponse } from './security-provider.utils';
describe('security-provider util', () => {
describe('isSuspiciousResponse', () => {
it('should return false if the response does not exist', () => {
const result = isSuspiciousResponse(undefined);
expect(result).toBeFalsy();
});
it('should return false when flagAsDangerous exists and is not malicious', () => {
const result = isSuspiciousResponse({
flagAsDangerous: SECURITY_PROVIDER_MESSAGE_SEVERITY.NOT_MALICIOUS,
});
expect(result).toBeFalsy();
});
it('should return true when flagAsDangerous exists and is malicious or not safe', () => {
const result = isSuspiciousResponse({
flagAsDangerous: SECURITY_PROVIDER_MESSAGE_SEVERITY.NOT_SAFE,
});
expect(result).toBeTruthy();
});
it('should return true if the response exists but is empty', () => {
const result = isSuspiciousResponse({});
expect(result).toBeTruthy();
});
});
});

View File

@ -0,0 +1,19 @@
import { Json } from '@metamask/utils';
import { SECURITY_PROVIDER_MESSAGE_SEVERITY } from '../constants/security-provider';
export function isSuspiciousResponse(
securityProviderResponse: Record<string, Json> | undefined,
): boolean {
if (!securityProviderResponse) {
return false;
}
const isFlagged =
securityProviderResponse.flagAsDangerous !== undefined &&
securityProviderResponse.flagAsDangerous !==
SECURITY_PROVIDER_MESSAGE_SEVERITY.NOT_MALICIOUS;
const isNotVerified = Object.keys(securityProviderResponse).length === 0;
return isFlagged || isNotVerified;
}

View File

@ -0,0 +1,35 @@
export const UI_INSTITUTIONAL_NOTIFICATIONS = {
1: {
id: 11,
date: '2022-08-28',
image: {
src: 'images/portfolio.svg',
},
hideDate: true,
descriptionInBullets: true,
},
};
export const getTranslatedInstitutionalUINotifications = (t, locale) => {
const formattedLocale = locale.replace('_', '-');
return {
1: {
...UI_INSTITUTIONAL_NOTIFICATIONS[11],
title: 'Portfolio dashboard',
description: [
'Portfolio snapshots',
'Filtering by account and network',
'Sector and protocol allocation',
'Improved navigation',
],
date: new Intl.DateTimeFormat(formattedLocale).format(
new Date(UI_INSTITUTIONAL_NOTIFICATIONS[11].date),
),
customButton: {
name: 'mmi-portfolio',
text: t('viewPortfolioDashboard'),
logo: true,
},
},
};
};

View File

@ -674,6 +674,19 @@ async function terminateServiceWorker(driver) {
await driver.closeWindowHandle(serviceWorkerTab);
}
/**
* This method assumes the extension is open, the dapp is open and waits for a
* third window handle to open (the notification window). Once it does it
* switches to the new window.
*
* @param {WebDriver} driver
*/
async function switchToNotificationWindow(driver) {
await driver.waitUntilXWindowHandles(3);
const windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('MetaMask Notification', windowHandles);
}
module.exports = {
DAPP_URL,
DAPP_ONE_URL,
@ -720,4 +733,5 @@ module.exports = {
switchToWindow,
sleepSeconds,
terminateServiceWorker,
switchToNotificationWindow,
};

View File

@ -0,0 +1,121 @@
const { strict: assert } = require('assert');
const {
defaultGanacheOptions,
switchToNotificationWindow,
withFixtures,
openDapp,
unlockWallet,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
/**
* mocks the segment api multiple times for specific payloads that we expect to
* see when these tests are run. In this case we are looking for
* 'Permissions Requested' and 'Permissions Received'. Do not use the constants
* from the metrics constants files, because if these change we want a strong
* indicator to our data team that the shape of data will change.
*
* @param {import('mockttp').Mockttp} mockServer
* @returns {Promise<import('mockttp/dist/pluggable-admin').MockttpClientResponse>[]}
*/
async function mockSegment(mockServer) {
return [
await mockServer
.forPost('https://api.segment.io/v1/batch')
.withJsonBodyIncluding({
batch: [{ type: 'track', event: 'Permissions Requested' }],
})
.thenCallback(() => {
return {
statusCode: 200,
};
}),
await mockServer
.forPost('https://api.segment.io/v1/batch')
.withJsonBodyIncluding({
batch: [{ type: 'track', event: 'Permissions Approved' }],
})
.thenCallback(() => {
return {
statusCode: 200,
};
}),
];
}
/**
* This method handles getting the mocked requests to the segment server
*
* @param {WebDriver} driver
* @param {import('mockttp').Mockttp} mockedEndpoints
* @returns {import('mockttp/dist/pluggable-admin').MockttpClientResponse[]}
*/
async function getEventPayloads(driver, mockedEndpoints) {
await driver.wait(async () => {
let isPending = true;
for (const mockedEndpoint of mockedEndpoints) {
isPending = await mockedEndpoint.isPending();
}
return isPending === false;
}, 10000);
const mockedRequests = [];
for (const mockedEndpoint of mockedEndpoints) {
mockedRequests.push(...(await mockedEndpoint.getSeenRequests()));
}
return mockedRequests.map((req) => req.body.json.batch).flat();
}
describe('Permissions Approved Event', function () {
it('Successfully tracked when connecting to dapp', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withMetaMetricsController({
metaMetricsId: 'fake-metrics-id',
participateInMetaMetrics: true,
})
.build(),
defaultGanacheOptions,
title: this.test.title,
testSpecificMock: mockSegment,
},
async ({ driver, mockedEndpoint: mockedEndpoints }) => {
await driver.navigate();
await unlockWallet(driver);
await openDapp(driver);
await driver.clickElement({
text: 'Connect',
tag: 'button',
});
await switchToNotificationWindow(driver);
await driver.clickElement({
text: 'Next',
tag: 'button',
});
await driver.clickElement({
text: 'Connect',
tag: 'button',
});
const events = await getEventPayloads(driver, mockedEndpoints);
assert.deepStrictEqual(events[0].properties, {
method: 'eth_requestAccounts',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
assert.deepStrictEqual(events[1].properties, {
method: 'eth_requestAccounts',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
},
);
});
});

View File

@ -0,0 +1,323 @@
const { strict: assert } = require('assert');
const {
defaultGanacheOptions,
switchToNotificationWindow,
withFixtures,
regularDelayMs,
openDapp,
unlockWallet,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
/**
* mocks the segment api multiple times for specific payloads that we expect to
* see when these tests are run. In this case we are looking for
* 'Signature Requested' and 'Signature Received'. Do not use the constants
* from the metrics constants files, because if these change we want a strong
* indicator to our data team that the shape of data will change.
*
* @param {import('mockttp').Mockttp} mockServer
* @returns {Promise<import('mockttp/dist/pluggable-admin').MockttpClientResponse>[]}
*/
async function mockSegment(mockServer) {
return [
await mockServer
.forPost('https://api.segment.io/v1/batch')
.withJsonBodyIncluding({
batch: [{ type: 'track', event: 'Signature Requested' }],
})
.thenCallback(() => {
return {
statusCode: 200,
};
}),
await mockServer
.forPost('https://api.segment.io/v1/batch')
.withJsonBodyIncluding({
batch: [{ type: 'track', event: 'Signature Approved' }],
})
.thenCallback(() => {
return {
statusCode: 200,
};
}),
];
}
/**
* Some signing methods have extra security that requires the user to click a
* button to validate that they have verified the details. This method handles
* performing the necessary steps to click that button.
*
* @param {WebDriver} driver
*/
async function validateContractDetails(driver) {
const verifyContractDetailsButton = await driver.findElement(
'.signature-request-content__verify-contract-details',
);
verifyContractDetailsButton.click();
await driver.clickElement({ text: 'Got it', tag: 'button' });
// Approve signing typed data
await driver.clickElement('[data-testid="signature-request-scroll-button"]');
await driver.delay(regularDelayMs);
}
/**
* This method handles clicking the sign button on signature confrimation
* screen.
*
* @param {WebDriver} driver
*/
async function clickSignOnSignatureConfirmation(driver) {
await driver.clickElement({ text: 'Sign', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
await driver.getAllWindowHandles();
}
/**
* This method handles getting the mocked requests to the segment server
*
* @param {WebDriver} driver
* @param {import('mockttp').Mockttp} mockedEndpoints
* @returns {import('mockttp/dist/pluggable-admin').MockttpClientResponse[]}
*/
async function getEventPayloads(driver, mockedEndpoints) {
await driver.wait(async () => {
let isPending = true;
for (const mockedEndpoint of mockedEndpoints) {
isPending = await mockedEndpoint.isPending();
}
return isPending === false;
}, 10000);
const mockedRequests = [];
for (const mockedEndpoint of mockedEndpoints) {
mockedRequests.push(...(await mockedEndpoint.getSeenRequests()));
}
return mockedRequests.map((req) => req.body.json.batch).flat();
}
describe('Signature Approved Event', function () {
it('Successfully tracked for signTypedData_v4', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.withMetaMetricsController({
metaMetricsId: 'fake-metrics-id',
participateInMetaMetrics: true,
})
.build(),
defaultGanacheOptions,
title: this.test.title,
testSpecificMock: mockSegment,
},
async ({ driver, mockedEndpoint: mockedEndpoints }) => {
await driver.navigate();
await unlockWallet(driver);
await openDapp(driver);
// creates a sign typed data signature request
await driver.clickElement('#signTypedDataV4');
await switchToNotificationWindow(driver);
await validateContractDetails(driver);
await clickSignOnSignatureConfirmation(driver);
const events = await getEventPayloads(driver, mockedEndpoints);
assert.deepStrictEqual(events[0].properties, {
signature_type: 'eth_signTypedData_v4',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
assert.deepStrictEqual(events[1].properties, {
signature_type: 'eth_signTypedData_v4',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
},
);
});
it('Successfully tracked for signTypedData_v3', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.withMetaMetricsController({
metaMetricsId: 'fake-metrics-id',
participateInMetaMetrics: true,
})
.build(),
defaultGanacheOptions,
title: this.test.title,
testSpecificMock: mockSegment,
},
async ({ driver, mockedEndpoint: mockedEndpoints }) => {
await driver.navigate();
await unlockWallet(driver);
await openDapp(driver);
// creates a sign typed data signature request
await driver.clickElement('#signTypedDataV3');
await switchToNotificationWindow(driver);
await validateContractDetails(driver);
await clickSignOnSignatureConfirmation(driver);
const events = await getEventPayloads(driver, mockedEndpoints);
assert.deepStrictEqual(events[0].properties, {
signature_type: 'eth_signTypedData_v3',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
assert.deepStrictEqual(events[1].properties, {
signature_type: 'eth_signTypedData_v3',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
},
);
});
it('Successfully tracked for signTypedData', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.withMetaMetricsController({
metaMetricsId: 'fake-metrics-id',
participateInMetaMetrics: true,
})
.build(),
defaultGanacheOptions,
title: this.test.title,
testSpecificMock: mockSegment,
},
async ({ driver, mockedEndpoint: mockedEndpoints }) => {
await driver.navigate();
await unlockWallet(driver);
await openDapp(driver);
// creates a sign typed data signature request
await driver.clickElement('#signTypedData');
await switchToNotificationWindow(driver);
await clickSignOnSignatureConfirmation(driver);
const events = await getEventPayloads(driver, mockedEndpoints);
assert.deepStrictEqual(events[0].properties, {
signature_type: 'eth_signTypedData',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
assert.deepStrictEqual(events[1].properties, {
signature_type: 'eth_signTypedData',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
},
);
});
it('Successfully tracked for personalSign', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.withMetaMetricsController({
metaMetricsId: 'fake-metrics-id',
participateInMetaMetrics: true,
})
.build(),
defaultGanacheOptions,
title: this.test.title,
testSpecificMock: mockSegment,
},
async ({ driver, mockedEndpoint: mockedEndpoints }) => {
await driver.navigate();
await unlockWallet(driver);
await openDapp(driver);
// creates a sign typed data signature request
await driver.clickElement('#personalSign');
await switchToNotificationWindow(driver);
await clickSignOnSignatureConfirmation(driver);
const events = await getEventPayloads(driver, mockedEndpoints);
assert.deepStrictEqual(events[0].properties, {
signature_type: 'personal_sign',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
assert.deepStrictEqual(events[1].properties, {
signature_type: 'personal_sign',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
},
);
});
it('Successfully tracked for eth_sign', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.withPreferencesController({
disabledRpcMethodPreferences: {
eth_sign: true,
},
})
.withMetaMetricsController({
metaMetricsId: 'fake-metrics-id',
participateInMetaMetrics: true,
})
.build(),
defaultGanacheOptions,
title: this.test.title,
testSpecificMock: mockSegment,
},
async ({ driver, mockedEndpoint: mockedEndpoints }) => {
await driver.navigate();
await unlockWallet(driver);
await openDapp(driver);
// creates a sign typed data signature request
await driver.clickElement('#ethSign');
await switchToNotificationWindow(driver);
await driver.delay(regularDelayMs);
await driver.clickElement('[data-testid="page-container-footer-next"]');
await driver.clickElement(
'.signature-request-warning__footer__sign-button',
);
const events = await getEventPayloads(driver, mockedEndpoints);
assert.deepStrictEqual(events[0].properties, {
signature_type: 'eth_sign',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
assert.deepStrictEqual(events[1].properties, {
signature_type: 'eth_sign',
category: 'inpage_provider',
locale: 'en',
chain_id: '0x539',
environment_type: 'background',
});
},
);
});
});

View File

@ -35,7 +35,7 @@ describe('Import ERC1155 NFT', function () {
// After login, go to NFTs tab, open the import NFT/ERC1155 form
await driver.clickElement('[data-testid="home__nfts-tab"]');
await driver.clickElement({ text: 'Import NFTs', tag: 'a' });
await driver.clickElement({ text: 'Import NFT', tag: 'button' });
// Enter a valid NFT that belongs to user and check success message appears
await driver.fill('[data-testid="address"]', contractAddress);
@ -83,7 +83,7 @@ describe('Import ERC1155 NFT', function () {
// After login, go to NFTs tab, open the import NFT form
await driver.clickElement('[data-testid="home__nfts-tab"]');
await driver.clickElement({ text: 'Import NFTs', tag: 'a' });
await driver.clickElement({ text: 'Import NFT', tag: 'button' });
// Enter an NFT that not belongs to user with a valid address and an invalid token id
await driver.fill('[data-testid="address"]', contractAddress);

View File

@ -35,7 +35,7 @@ describe('Import NFT', function () {
// After login, go to NFTs tab, open the import NFT form
await driver.clickElement('[data-testid="home__nfts-tab"]');
await driver.clickElement({ text: 'Import NFTs', tag: 'a' });
await driver.clickElement({ text: 'Import NFT', tag: 'button' });
// Enter a valid NFT that belongs to user and check success message appears
await driver.fill('[data-testid="address"]', contractAddress);
@ -82,7 +82,7 @@ describe('Import NFT', function () {
// After login, go to NFTs tab, open the import NFT form
await driver.clickElement('[data-testid="home__nfts-tab"]');
await driver.clickElement({ text: 'Import NFTs', tag: 'a' });
await driver.clickElement({ text: 'Import NFT', tag: 'button' });
// Enter an NFT that not belongs to user with a valid address and an invalid token id
await driver.fill('[data-testid="address"]', contractAddress);

View File

@ -72,6 +72,7 @@ async function main() {
...(await getTestPathsForTestDir(testDir)),
...(await getTestPathsForTestDir(path.join(__dirname, 'swaps'))),
...(await getTestPathsForTestDir(path.join(__dirname, 'nft'))),
...(await getTestPathsForTestDir(path.join(__dirname, 'metrics'))),
path.join(__dirname, 'metamask-ui.spec.js'),
];

View File

@ -171,7 +171,7 @@ describe('Address Book', function () {
await driver.clickElement({ text: 'Test Name 1', tag: 'p' });
await driver.clickElement({ text: 'Edit', tag: 'button' });
await driver.clickElement({ text: 'Delete account', tag: 'a' });
await driver.clickElement({ text: 'Delete contact', tag: 'a' });
// it checks if account is deleted
const contact = await driver.findElement(
'.send__select-recipient-wrapper__group-item',

View File

@ -2,6 +2,59 @@ const { strict: assert } = require('assert');
const { convertToHexValue, withFixtures, openDapp } = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
async function getEncryptionKey(driver) {
await driver.clickElement('#getEncryptionKeyButton');
await driver.waitUntilXWindowHandles(3);
let windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('MetaMask Notification', windowHandles);
await driver.waitForSelector({
css: '.request-encryption-public-key__header__text',
text: 'Request encryption public key',
});
await driver.clickElement({ text: 'Provide', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
return await driver.findElement('#encryptionKeyDisplay');
}
async function encryptMessage(driver, message) {
await driver.fill('#encryptMessageInput', message);
await driver.clickElement('#encryptButton');
await driver.waitForSelector({
css: '#ciphertextDisplay',
text: '0x',
});
}
async function decryptMessage(driver) {
await driver.clickElement('#decryptButton');
await driver.waitUntilXWindowHandles(3);
const windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('MetaMask Notification', windowHandles);
await driver.waitForSelector({
css: '.request-decrypt-message__header__text',
text: 'Decrypt request',
});
}
async function verifyDecryptedMessageMM(driver, message) {
await driver.clickElement({ text: 'Decrypt message', tag: 'div' });
const notificationMessage = await driver.isElementPresent({
text: message,
tag: 'div',
});
assert.equal(notificationMessage, true);
await driver.clickElement({ text: 'Decrypt', tag: 'button' });
}
async function verifyDecryptedMessageDapp(driver, message) {
const windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
const clearTextLabel = await driver.findElement('#cleartextDisplay');
assert.equal(await clearTextLabel.getText(), message);
}
describe('Encrypt Decrypt', function () {
const ganacheOptions = {
accounts: [
@ -31,66 +84,85 @@ describe('Encrypt Decrypt', function () {
await openDapp(driver);
// ------ Get Encryption key ------
await driver.clickElement('#getEncryptionKeyButton');
await driver.waitUntilXWindowHandles(3);
let windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.waitForSelector({
css: '.request-encryption-public-key__header__text',
text: 'Request encryption public key',
});
await driver.clickElement({ text: 'Provide', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
const encryptionKeyLabel = await driver.findElement(
'#encryptionKeyDisplay',
);
const encryptionKeyLabel = await getEncryptionKey(driver);
assert.equal(await encryptionKeyLabel.getText(), encryptionKey);
// ------ Encrypt ------
await driver.fill('#encryptMessageInput', message);
await driver.clickElement('#encryptButton');
await driver.waitForSelector({
css: '#ciphertextDisplay',
text: '0x',
});
await encryptMessage(driver, message);
// ------ Decrypt ------
await driver.clickElement('#decryptButton');
await driver.waitUntilXWindowHandles(3);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.waitForSelector({
css: '.request-decrypt-message__header__text',
text: 'Decrypt request',
});
await decryptMessage(driver);
// Account balance is converted properly
const decryptAccountBalanceLabel = await driver.findElement(
'.request-decrypt-message__balance-value',
);
assert.equal(await decryptAccountBalanceLabel.getText(), '25 ETH');
// Verify message in MetaMask Notification
await driver.clickElement({ text: 'Decrypt message', tag: 'div' });
const notificationMessage = await driver.isElementPresent({
text: message,
tag: 'div',
});
assert.equal(notificationMessage, true);
await driver.clickElement({ text: 'Decrypt', tag: 'button' });
await verifyDecryptedMessageMM(driver, message);
// Verify message in Test Dapp
await driver.waitUntilXWindowHandles(2);
windowHandles = await driver.getAllWindowHandles();
await verifyDecryptedMessageDapp(driver, message);
},
);
});
it('should encrypt and decrypt multiple messages', async function () {
const message2 = 'Hello, Alice!';
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions,
title: this.test.title,
},
async ({ driver }) => {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await openDapp(driver);
// ------ Get Encryption key ------
const encryptionKeyLabel = await getEncryptionKey(driver);
assert.equal(await encryptionKeyLabel.getText(), encryptionKey);
// ------ Encrypt Message 1------
await encryptMessage(driver, message);
// ------ Decrypt Message 1 ------
await decryptMessage(driver);
// ------ Switch to Dapp ------
let windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
const clearTextLabel = await driver.findElement('#cleartextDisplay');
assert.equal(await clearTextLabel.getText(), message);
// ------ Encrypt Message 2------
await encryptMessage(driver, message2);
// ------ Decrypt Message 2 ------
await decryptMessage(driver);
// Verify message 1 in MetaMask Notification
await verifyDecryptedMessageMM(driver, message);
// Verify message 1 in Test Dapp
await verifyDecryptedMessageDapp(driver, message);
// ------ Switch to Dapp ------
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
// Verify message 2 in MetaMask Notification
await verifyDecryptedMessageMM(driver, message2);
// Verify message 2 in Test Dapp
await verifyDecryptedMessageDapp(driver, message2);
},
);
});

View File

@ -5,7 +5,8 @@ const FixtureBuilder = require('../fixture-builder');
describe('Sentry errors', function () {
async function mockSentry(mockServer) {
return await mockServer
.forPost('https://sentry.io/api/0000000/store/')
.forPost('https://sentry.io/api/0000000/envelope/')
.withBodyIncluding('Test Error')
.thenCallback(() => {
return {
statusCode: 200,
@ -48,7 +49,8 @@ describe('Sentry errors', function () {
return isPending === false;
}, 10000);
const [mockedRequest] = await mockedEndpoint.getSeenRequests();
const mockJsonBody = mockedRequest.body.json;
const mockTextBody = mockedRequest.body.text.split('\n');
const mockJsonBody = JSON.parse(mockTextBody[2]);
const { level, extra } = mockJsonBody;
const [{ type, value }] = mockJsonBody.exception.values;
const { participateInMetaMetrics } = extra.appState.store.metamask;

View File

@ -5,6 +5,7 @@ const {
DAPP_URL,
defaultGanacheOptions,
unlockWallet,
regularDelayMs,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
@ -36,7 +37,7 @@ describe('Eth sign', function () {
});
it('can initiate and confirm a eth sign', async function () {
const expectedPersonalMessage =
const expectedEthSignMessage =
'0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0';
const expectedEthSignResult =
'"0x816ab6c5d5356548cc4e004ef35a37fdfab916742a2bbeda756cd064c3d3789a6557d41d49549be1de249e1937a8d048996dfcc70d0552111605dc7cc471e8531b"';
@ -69,23 +70,11 @@ describe('Eth sign', function () {
windowHandles,
);
await driver.findElement({
css: '.request-signature__content__title',
text: 'Signature request',
});
await verifyAndAssertEthSign(driver, DAPP_URL, expectedEthSignMessage);
await driver.findElement({
css: '.request-signature__origin',
text: DAPP_URL,
});
await driver.findElement({
css: '.request-signature__row-value',
text: expectedPersonalMessage,
});
await driver.clickElement('[data-testid="page-container-footer-next"]');
await driver.clickElement(
await approveEthSign(
driver,
'[data-testid="page-container-footer-next"]',
'.signature-request-warning__footer__sign-button',
);
// Switch to the Dapp
@ -101,4 +90,103 @@ describe('Eth sign', function () {
},
);
});
it('can queue multiple eth sign and confirm', async function () {
const expectedEthSignMessage =
'0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0';
const expectedEthSignResult =
'"0x816ab6c5d5356548cc4e004ef35a37fdfab916742a2bbeda756cd064c3d3789a6557d41d49549be1de249e1937a8d048996dfcc70d0552111605dc7cc471e8531b"';
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPreferencesController({
disabledRpcMethodPreferences: {
eth_sign: true,
},
})
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions: defaultGanacheOptions,
title: this.test.title,
},
async ({ driver }) => {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await openDapp(driver);
// Create eth sign
await driver.clickElement('#ethSign');
// Wait for Signature request popup
await driver.waitUntilXWindowHandles(3);
let windowHandles = await driver.getAllWindowHandles();
// Switch to Dapp
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
// Create second eth sign
await driver.clickElement('#ethSign');
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.waitForSelector({
text: 'Reject 2 requests',
tag: 'a',
});
await verifyAndAssertEthSign(driver, DAPP_URL, expectedEthSignMessage);
// Confirm first eth sign
await approveEthSign(
driver,
'[data-testid="page-container-footer-next"]',
'.signature-request-warning__footer__sign-button',
);
// Confirm second eth sign
await approveEthSign(
driver,
'[data-testid="page-container-footer-next"]',
'.signature-request-warning__footer__sign-button',
);
// Switch to the Dapp
await driver.waitUntilXWindowHandles(2);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
// Verify last confirmed request
const result = await driver.findElement('#ethSignResult');
assert.equal(await result.getText(), expectedEthSignResult);
},
);
});
});
async function verifyAndAssertEthSign(driver, dappUrl, expectedMessage) {
await driver.findElement({
css: '.request-signature__content__title',
text: 'Signature request',
});
await driver.findElement({
css: '.request-signature__origin',
text: dappUrl,
});
await driver.findElement({
css: '.request-signature__row-value',
text: expectedMessage,
});
}
async function approveEthSign(driver, buttonTestId, signButtonClass) {
await driver.clickElement(buttonTestId);
await driver.clickElement(signButtonClass);
await driver.delay(regularDelayMs);
}

View File

@ -0,0 +1,171 @@
const {
convertToHexValue,
withFixtures,
logInWithBalanceValidation,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('Gas estimates generated by MetaMask', function () {
const baseGanacheOptions = {
accounts: [
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: convertToHexValue(25000000000000000000),
},
],
};
const preLondonGanacheOptions = {
...baseGanacheOptions,
hardfork: 'berlin',
};
const postLondonGanacheOptions = {
...baseGanacheOptions,
hardfork: 'london',
};
describe('Send on a network that is EIP-1559 compatible', function () {
it('show expected gas defaults', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
ganacheOptions: postLondonGanacheOptions,
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
await driver.navigate();
await logInWithBalanceValidation(driver, ganacheServer);
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill(
'input[placeholder="Enter public address (0x) or ENS name"]',
'0x2f318C334780961FB129D2a6c30D0763d9a5C970',
);
await driver.fill('.unit-input__input', '1');
// Check that the gas estimation is what we expect
await driver.findElement({
cass: '[data-testid="confirm-gas-display"]',
text: '0.00043983',
});
},
);
});
it('show expected gas defaults when API is down', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
ganacheOptions: postLondonGanacheOptions,
testSpecificMock: (mockServer) => {
mockServer
.forGet(
'https://gas-api.metaswap.codefi.network/networks/1337/suggestedGasFees',
)
.thenCallback(() => {
return {
json: {
error: 'cannot get gas prices for chain id 1337',
},
statusCode: 503,
};
});
},
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
await driver.navigate();
await logInWithBalanceValidation(driver, ganacheServer);
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill(
'input[placeholder="Enter public address (0x) or ENS name"]',
'0x2f318C334780961FB129D2a6c30D0763d9a5C970',
);
await driver.fill('.unit-input__input', '1');
// Check that the gas estimation is what we expect
await driver.findElement({
cass: '[data-testid="confirm-gas-display"]',
text: '0.00043983',
});
},
);
});
it('show expected gas defaults when the network is not supported', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
ganacheOptions: postLondonGanacheOptions,
testSpecificMock: (mockServer) => {
mockServer
.forGet(
'https://gas-api.metaswap.codefi.network/networks/1337/suggestedGasFees',
)
.thenCallback(() => {
return {
statusCode: 422,
};
});
},
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
await driver.navigate();
await logInWithBalanceValidation(driver, ganacheServer);
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill(
'input[placeholder="Enter public address (0x) or ENS name"]',
'0x2f318C334780961FB129D2a6c30D0763d9a5C970',
);
await driver.fill('.unit-input__input', '1');
// Check that the gas estimation is what we expect
await driver.findElement({
cass: '[data-testid="confirm-gas-display"]',
text: '0.00043983',
});
},
);
});
});
describe('Send on a network that is not EIP-1559 compatible', function () {
it('show expected gas defaults', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
ganacheOptions: preLondonGanacheOptions,
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
await driver.navigate();
await logInWithBalanceValidation(driver, ganacheServer);
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill(
'input[placeholder="Enter public address (0x) or ENS name"]',
'0x2f318C334780961FB129D2a6c30D0763d9a5C970',
);
await driver.fill('.unit-input__input', '1');
// Check that the gas estimation is what we expect
await driver.findElement({
cass: '[data-testid="confirm-gas-display"]',
text: '0.000042',
});
},
);
});
});
});

View File

@ -1,5 +1,10 @@
const { strict: assert } = require('assert');
const { convertToHexValue, withFixtures, openDapp } = require('../helpers');
const {
convertToHexValue,
withFixtures,
openDapp,
regularDelayMs,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('Personal sign', function () {
@ -33,7 +38,7 @@ describe('Personal sign', function () {
await driver.clickElement('#personalSign');
await driver.waitUntilXWindowHandles(3);
let windowHandles = await driver.getAllWindowHandles();
const windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
@ -47,23 +52,92 @@ describe('Personal sign', function () {
await driver.clickElement('[data-testid="page-container-footer-next"]');
// Switch to the Dapp
await driver.waitUntilXWindowHandles(2);
windowHandles = await driver.getAllWindowHandles();
await verifyAndAssertPersonalMessage(driver, publicAddress);
},
);
});
it('can queue multiple personal signs and confirm', async function () {
const ganacheOptions = {
accounts: [
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: convertToHexValue(25000000000000000000),
},
],
};
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions,
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
const addresses = await ganacheServer.getAccounts();
const publicAddress = addresses[0];
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await openDapp(driver);
// Create personal sign
await driver.clickElement('#personalSign');
await driver.waitUntilXWindowHandles(3);
const windowHandles = await driver.getAllWindowHandles();
// Switch to Dapp
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
// Verify
await driver.clickElement('#personalSignVerify');
const verifySigUtil = await driver.findElement(
'#personalSignVerifySigUtilResult',
// Create second personal sign
await driver.clickElement('#personalSign');
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
const verifyECRecover = await driver.waitForSelector({
css: '#personalSignVerifyECRecoverResult',
text: publicAddress,
await driver.waitForSelector({
text: 'Reject 2 requests',
tag: 'a',
});
assert.equal(await verifySigUtil.getText(), publicAddress);
assert.equal(await verifyECRecover.getText(), publicAddress);
const personalMessageRow = await driver.findElement(
'.request-signature__row-value',
);
const personalMessage = await personalMessageRow.getText();
assert.equal(personalMessage, 'Example `personal_sign` message');
// Confirm first personal sign
await driver.clickElement('[data-testid="page-container-footer-next"]');
await driver.delay(regularDelayMs);
// Confirm second personal sign
await driver.clickElement('[data-testid="page-container-footer-next"]');
await verifyAndAssertPersonalMessage(driver, publicAddress);
},
);
});
});
async function verifyAndAssertPersonalMessage(driver, publicAddress) {
// Switch to the Dapp
await driver.waitUntilXWindowHandles(2);
const windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
// Verify last confirmed personal sign
await driver.clickElement('#personalSignVerify');
const verifySigUtil = await driver.findElement(
'#personalSignVerifySigUtilResult',
);
const verifyECRecover = await driver.waitForSelector({
css: '#personalSignVerifyECRecoverResult',
text: publicAddress,
});
assert.equal(await verifySigUtil.getText(), publicAddress);
assert.equal(await verifyECRecover.getText(), publicAddress);
}

View File

@ -26,7 +26,7 @@ describe('Portfolio site', function () {
await driver.press('#password', driver.Key.ENTER);
// Click Portfolio site
await driver.clickElement('[data-testid="home__portfolio-site"]');
await driver.clickElement('[data-testid="eth-overview-portfolio"]');
await driver.waitUntilXWindowHandles(2);
const windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
@ -34,7 +34,7 @@ describe('Portfolio site', function () {
// Verify site
assert.equal(
await driver.getCurrentUrl(),
'http://127.0.0.1:8080/?metamaskEntry=ext&metametricsId=null',
'http://127.0.0.1:8080/?metamaskEntry=ext_portfolio_button&metametricsId=null',
);
},
);

View File

@ -1,240 +1,238 @@
const { strict: assert } = require('assert');
const {
convertToHexValue,
withFixtures,
regularDelayMs,
openDapp,
DAPP_URL,
convertToHexValue,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('Sign Typed Data V4 Signature Request', function () {
it('can initiate and confirm a Signature Request', async function () {
const ganacheOptions = {
accounts: [
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: convertToHexValue(25000000000000000000),
},
],
};
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions,
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
const addresses = await ganacheServer.getAccounts();
const publicAddress = addresses[0];
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await openDapp(driver);
// creates a sign typed data signature request
await driver.clickElement('#signTypedDataV4');
await driver.waitUntilXWindowHandles(3);
let windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
const title = await driver.findElement(
'.signature-request__content__title',
);
const origin = await driver.findElement('.signature-request__origin');
const verifyContractDetailsButton = await driver.findElement(
'.signature-request-content__verify-contract-details',
);
const message = await driver.findElement(
'.signature-request-data__node__value',
);
assert.equal(await title.getText(), 'Signature request');
assert.equal(await origin.getText(), DAPP_URL);
verifyContractDetailsButton.click();
await driver.findElement({ text: 'Third-party details', tag: 'h5' });
await driver.findElement('[data-testid="recipient"]');
await driver.clickElement({ text: 'Got it', tag: 'button' });
assert.equal(await message.getText(), 'Hello, Bob!');
// Approve signing typed data
await driver.clickElement(
'[data-testid="signature-request-scroll-button"]',
);
await driver.delay(regularDelayMs);
await driver.clickElement({ text: 'Sign', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
windowHandles = await driver.getAllWindowHandles();
// switch to the Dapp and verify the signed addressed
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
await driver.clickElement('#signTypedDataV4Verify');
const recoveredAddress = await driver.findElement(
'#signTypedDataV4VerifyResult',
);
assert.equal(await recoveredAddress.getText(), publicAddress);
},
);
});
});
/* eslint-disable-next-line mocha/max-top-level-suites */
describe('Sign Typed Data V3 Signature Request', function () {
it('can initiate and confirm a Signature Request', async function () {
const ganacheOptions = {
accounts: [
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: convertToHexValue(25000000000000000000),
},
],
};
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions,
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
const addresses = await ganacheServer.getAccounts();
const publicAddress = addresses[0];
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await openDapp(driver);
// creates a sign typed data signature request
await driver.clickElement('#signTypedDataV3');
await driver.waitUntilXWindowHandles(3);
let windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
const title = await driver.findElement(
'.signature-request__content__title',
);
const origin = await driver.findElement('.signature-request__origin');
const verifyContractDetailsButton = await driver.findElement(
'.signature-request-content__verify-contract-details',
);
const messages = await driver.findElements(
'.signature-request-data__node__value',
);
assert.equal(await title.getText(), 'Signature request');
assert.equal(await origin.getText(), DAPP_URL);
verifyContractDetailsButton.click();
await driver.findElement({ text: 'Third-party details', tag: 'h5' });
await driver.findElement('[data-testid="recipient"]');
await driver.clickElement({ text: 'Got it', tag: 'button' });
assert.equal(await messages[4].getText(), 'Hello, Bob!');
// Approve signing typed data
await driver.clickElement(
'[data-testid="signature-request-scroll-button"]',
);
await driver.delay(regularDelayMs);
await driver.clickElement({ text: 'Sign', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
windowHandles = await driver.getAllWindowHandles();
// switch to the Dapp and verify the signed addressed
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
await driver.clickElement('#signTypedDataV3Verify');
const recoveredAddress = await driver.findElement(
'#signTypedDataV3VerifyResult',
);
assert.equal(await recoveredAddress.getText(), publicAddress);
},
);
});
});
const signatureRequestType = {
signTypedData: 'Sign Typed Data',
signTypedDataV3: 'Sign Typed Data V3',
signTypedDataV4: 'Sign Typed Data V4',
};
const testData = [
{
type: signatureRequestType.signTypedData,
buttonId: '#signTypedData',
verifyId: '#signTypedDataVerify',
verifyResultId: '#signTypedDataVerifyResult',
expectedMessage: 'Hi, Alice!',
verifyAndAssertMessage: {
titleClass: '.request-signature__content__title',
originClass: '.request-signature__origin',
messageClass: '.request-signature__row-value',
},
},
{
type: signatureRequestType.signTypedDataV3,
buttonId: '#signTypedDataV3',
verifyId: '#signTypedDataV3Verify',
verifyResultId: '#signTypedDataV3VerifyResult',
expectedMessage: 'Hello, Bob!',
verifyAndAssertMessage: {
titleClass: '.signature-request__content__title',
originClass: '.signature-request__origin',
messageClass: '.signature-request-data__node__value',
},
},
{
type: signatureRequestType.signTypedDataV4,
buttonId: '#signTypedDataV4',
verifyId: '#signTypedDataV4Verify',
verifyResultId: '#signTypedDataV4VerifyResult',
expectedMessage: 'Hello, Bob!',
verifyAndAssertMessage: {
titleClass: '.signature-request__content__title',
originClass: '.signature-request__origin',
messageClass: '.signature-request-data__node__value',
},
},
];
const ganacheOptions = {
accounts: [
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: convertToHexValue(25000000000000000000),
},
],
};
describe('Sign Typed Data Signature Request', function () {
it('can initiate and confirm a Signature Request', async function () {
const ganacheOptions = {
accounts: [
testData.forEach((data) => {
it(`can initiate and confirm a Signature Request of ${data.type}`, async function () {
await withFixtures(
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: convertToHexValue(25000000000000000000),
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions,
title: this.test.title,
},
],
};
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions,
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
const addresses = await ganacheServer.getAccounts();
const publicAddress = addresses[0];
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
async ({ driver, ganacheServer }) => {
const addresses = await ganacheServer.getAccounts();
const publicAddress = addresses[0];
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await openDapp(driver);
await openDapp(driver);
// creates a sign typed data signature request
await driver.clickElement('#signTypedData');
// creates a sign typed data signature request
await driver.clickElement(data.buttonId);
await driver.waitUntilXWindowHandles(3);
let windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.waitUntilXWindowHandles(3);
let windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
const title = await driver.findElement(
'.request-signature__content__title',
);
const origin = await driver.findElement('.request-signature__origin');
const message = await driver.findElements(
'.request-signature__row-value',
);
assert.equal(await title.getText(), 'Signature request');
assert.equal(await origin.getText(), DAPP_URL);
assert.equal(await message[0].getText(), 'Hi, Alice!');
assert.equal(await message[1].getText(), '1337');
await verifyAndAssertSignTypedData(
driver,
data.type,
data.verifyAndAssertMessage.titleClass,
data.verifyAndAssertMessage.originClass,
data.verifyAndAssertMessage.messageClass,
data.expectedMessage,
);
// Approve signing typed data
await driver.clickElement({ text: 'Sign', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
windowHandles = await driver.getAllWindowHandles();
// Approve signing typed data
await approveSignatureRequest(
driver,
data.type,
'[data-testid="signature-request-scroll-button"]',
);
await driver.waitUntilXWindowHandles(2);
windowHandles = await driver.getAllWindowHandles();
// switch to the Dapp and verify the signed addressed
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
await driver.clickElement('#signTypedDataVerify');
const recoveredAddress = await driver.findElement(
'#signTypedDataVerifyResult',
);
assert.equal(await recoveredAddress.getText(), publicAddress);
},
);
// switch to the Dapp and verify the signed address
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
await driver.clickElement(data.verifyId);
const recoveredAddress = await driver.findElement(
data.verifyResultId,
);
assert.equal(await recoveredAddress.getText(), publicAddress);
},
);
});
});
testData.forEach((data) => {
it(`can queue multiple Signature Requests of ${data.type} and confirm`, async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions,
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
const addresses = await ganacheServer.getAccounts();
const publicAddress = addresses[0];
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await openDapp(driver);
// creates multiple sign typed data signature requests
await driver.clickElement(data.buttonId);
await driver.waitUntilXWindowHandles(3);
const windowHandles = await driver.getAllWindowHandles();
// switches to Dapp
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
// creates second sign typed data signature request
await driver.clickElement(data.buttonId);
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.waitForSelector({
text: 'Reject 2 requests',
tag: 'a',
});
await verifyAndAssertSignTypedData(
driver,
data.type,
data.verifyAndAssertMessage.titleClass,
data.verifyAndAssertMessage.originClass,
data.verifyAndAssertMessage.messageClass,
data.expectedMessage,
);
// approve first signature request
await approveSignatureRequest(
driver,
data.type,
'[data-testid="signature-request-scroll-button"]',
);
await driver.waitUntilXWindowHandles(3);
// approve second signature request
await approveSignatureRequest(
driver,
data.type,
'[data-testid="signature-request-scroll-button"]',
);
await driver.waitUntilXWindowHandles(2);
// switch to the Dapp and verify the signed address for each request
await driver.switchToWindowWithTitle('E2E Test Dapp');
await driver.clickElement(data.verifyId);
const recoveredAddress = await driver.findElement(
data.verifyResultId,
);
assert.equal(await recoveredAddress.getText(), publicAddress);
},
);
});
});
});
async function verifyAndAssertSignTypedData(
driver,
type,
titleClass,
originClass,
messageClass,
expectedMessage,
) {
const title = await driver.findElement(titleClass);
const origin = await driver.findElement(originClass);
assert.equal(await title.getText(), 'Signature request');
assert.equal(await origin.getText(), DAPP_URL);
const messages = await driver.findElements(messageClass);
if (type !== signatureRequestType.signTypedData) {
const verifyContractDetailsButton = await driver.findElement(
'.signature-request-content__verify-contract-details',
);
verifyContractDetailsButton.click();
await driver.findElement({ text: 'Third-party details', tag: 'h5' });
await driver.findElement('[data-testid="recipient"]');
await driver.clickElement({ text: 'Got it', tag: 'button' });
}
const messageNumber = type === signatureRequestType.signTypedDataV3 ? 4 : 0;
assert.equal(await messages[messageNumber].getText(), expectedMessage);
}
async function approveSignatureRequest(driver, type, buttonElementId) {
if (type !== signatureRequestType.signTypedData) {
await driver.clickElement(buttonElementId);
}
await driver.delay(regularDelayMs);
await driver.clickElement({ text: 'Sign', tag: 'button' });
}

View File

@ -1,4 +1,4 @@
process.env.METAMASK_ENVIRONMENT = 'test';
process.env.SUPPORT_LINK = 'https://support.metamask.io';
process.env.IFRAME_EXECUTION_ENVIRONMENT_URL =
'https://execution.metamask.io/0.16.0-flask.1/index.html';
'https://execution.metamask.io/0.35.2-flask.1/index.html';

View File

@ -10,7 +10,6 @@ import {
getNativeCurrencyImage,
getDetectedTokensInCurrentNetwork,
getIstokenDetectionInactiveOnNonMainnetSupportedNetwork,
getTokenList,
} from '../../../selectors';
import { getNativeCurrency } from '../../../ducks/metamask/metamask';
import { useCurrencyDisplay } from '../../../hooks/useCurrencyDisplay';
@ -56,19 +55,15 @@ const AssetList = ({ onClickAsset }) => {
const primaryTokenImage = useSelector(getNativeCurrencyImage);
const detectedTokens = useSelector(getDetectedTokensInCurrentNetwork) || [];
const istokenDetectionInactiveOnNonMainnetSupportedNetwork = useSelector(
const isTokenDetectionInactiveOnNonMainnetSupportedNetwork = useSelector(
getIstokenDetectionInactiveOnNonMainnetSupportedNetwork,
);
const tokenList = useSelector(getTokenList);
const tokenData = Object.values(tokenList).find(
(token) => token.symbol === primaryCurrencyProperties.suffix,
);
const title = tokenData?.name || primaryCurrencyProperties.suffix;
return (
<>
<TokenListItem
onClick={() => onClickAsset(nativeCurrency)}
title={title}
title={nativeCurrency}
primary={
primaryCurrencyProperties.value ?? secondaryCurrencyProperties.value
}
@ -82,14 +77,14 @@ const AssetList = ({ onClickAsset }) => {
}}
/>
{detectedTokens.length > 0 &&
!istokenDetectionInactiveOnNonMainnetSupportedNetwork ? (
!isTokenDetectionInactiveOnNonMainnetSupportedNetwork ? (
<DetectedTokensBanner
actionButtonOnClick={() => setShowDetectedTokens(true)}
margin={4}
/>
) : null}
<Box marginTop={detectedTokens.length > 0 ? 0 : 4}>
<ImportTokenLink margin={4} />
<ImportTokenLink margin={4} marginBottom={2} />
</Box>
{showDetectedTokens && (
<DetectedToken setShowDetectedTokens={setShowDetectedTokens} />

View File

@ -1,11 +1,10 @@
import { useSelector } from 'react-redux';
import React, { useEffect } from 'react';
import { Text } from '../../component-library';
import { EditGasModes, PriorityLevels } from '../../../../shared/constants/gas';
import {
AlignItems,
DISPLAY,
FLEX_DIRECTION,
Display,
FlexDirection,
TextVariant,
} from '../../../helpers/constants/design-system';
import { getAppIsLoading } from '../../../selectors';
@ -16,10 +15,10 @@ import { useTransactionModalContext } from '../../../contexts/transaction-modal'
import EditGasFeeButton from '../edit-gas-fee-button';
import GasDetailsItem from '../gas-details-item';
import Box from '../../ui/box';
import Button from '../../ui/button';
import InfoTooltip from '../../ui/info-tooltip';
import Popover from '../../ui/popover';
import AppLoadingSpinner from '../app-loading-spinner';
import { Text, Button, ButtonLink } from '../../component-library';
const CancelSpeedupPopover = () => {
const {
@ -98,11 +97,11 @@ const CancelSpeedupPopover = () => {
<div className="cancel-speedup-popover__wrapper">
<Text
alignItems={AlignItems.center}
display={DISPLAY.FLEX}
display={Display.Flex}
variant={TextVariant.bodySm}
as="h6"
marginTop={0}
marginBottom={2}
paddingBottom={2}
className="cancel-speedup-popover__description"
>
{t('cancelSpeedUpLabel', [
<strong key="cancelSpeedupReplace">{t('replace')}</strong>,
@ -110,42 +109,39 @@ const CancelSpeedupPopover = () => {
<InfoTooltip
position="top"
contentText={
<Box>
{t('cancelSpeedUpTransactionTooltip', [
editGasMode === EditGasModes.cancel
? t('cancel')
: t('speedUp'),
])}
<div>
<a
href="https://community.metamask.io/t/how-to-speed-up-or-cancel-transactions-on-metamask/3296"
target="_blank"
rel="noopener noreferrer"
>
{t('learnMoreUpperCase')}
</a>
</div>
</Box>
<>
<Text variant={TextVariant.bodySm}>
{t('cancelSpeedUpTransactionTooltip', [
editGasMode === EditGasModes.cancel
? t('cancel')
: t('speedUp'),
])}
</Text>
<ButtonLink
variant={TextVariant.bodySm}
href="https://community.metamask.io/t/how-to-speed-up-or-cancel-transactions-on-metamask/3296"
target="_blank"
>
{t('learnMoreUpperCase')}
</ButtonLink>
</>
}
/>
</Text>
<div className="cancel-speedup-popover__separator" />
<Box
display={DISPLAY.FLEX}
display={Display.Flex}
alignItems={AlignItems.center}
flexDirection={FLEX_DIRECTION.COLUMN}
marginTop={4}
flexDirection={FlexDirection.Column}
marginTop={2}
>
<Box className="cancel-speedup-popover__edit-gas-button">
<div className="cancel-speedup-popover__edit-gas-button">
{!appIsLoading && <EditGasFeeButton />}
</Box>
<Box className="cancel-speedup-popover__gas-details">
</div>
<div className="cancel-speedup-popover__gas-details">
<GasDetailsItem />
</Box>
</div>
</Box>
<Button type="primary" onClick={submitTransactionChange}>
{t('submit')}
</Button>
<Button onClick={submitTransactionChange}>{t('submit')}</Button>
</div>
</Popover>
);

View File

@ -0,0 +1,80 @@
import React from 'react';
import { Provider } from 'react-redux';
import BigNumber from 'bignumber.js';
import configureStore from '../../../store/store';
import { TransactionModalContext } from '../../../contexts/transaction-modal';
import mockEstimates from '../../../../test/data/mock-estimates.json';
import mockState from '../../../../test/data/mock-state.json';
import {
EditGasModes,
GasEstimateTypes,
} from '../../../../shared/constants/gas';
import { decGWEIToHexWEI } from '../../../../shared/modules/conversion.utils';
import { GasFeeContextProvider } from '../../../contexts/gasFee';
import CancelSpeedupPopover from './cancel-speedup-popover';
const store = configureStore({
metamask: {
...mockState.metamask,
accounts: {
[mockState.metamask.selectedAddress]: {
address: mockState.metamask.selectedAddress,
balance: '0x1F4',
},
},
gasFeeEstimates: mockEstimates[GasEstimateTypes.feeMarket].gasFeeEstimates,
},
});
const MOCK_SUGGESTED_MEDIUM_MAXFEEPERGAS_DEC_GWEI =
mockEstimates[GasEstimateTypes.feeMarket].gasFeeEstimates.medium
.suggestedMaxFeePerGas;
const MOCK_SUGGESTED_MEDIUM_MAXFEEPERGAS_BN_WEI = new BigNumber(
decGWEIToHexWEI(MOCK_SUGGESTED_MEDIUM_MAXFEEPERGAS_DEC_GWEI),
16,
);
const MOCK_SUGGESTED_MEDIUM_MAXFEEPERGAS_HEX_WEI =
MOCK_SUGGESTED_MEDIUM_MAXFEEPERGAS_BN_WEI.toString(16);
export default {
title: 'Components/App/CancelSpeedupPopover',
component: CancelSpeedupPopover,
decorators: [
(story) => (
<Provider store={store}>
<GasFeeContextProvider
transaction={{
userFeeLevel: 'tenPercentIncreased',
txParams: {
gas: '0x5208',
maxFeePerGas: MOCK_SUGGESTED_MEDIUM_MAXFEEPERGAS_HEX_WEI,
maxPriorityFeePerGas: '0x59682f00',
},
}}
editGasMode={EditGasModes.cancel}
>
<TransactionModalContext.Provider
value={{
closeModal: () => undefined,
currentModal: 'cancelSpeedUpTransaction',
}}
>
{story()}
</TransactionModalContext.Provider>
</GasFeeContextProvider>
</Provider>
),
],
};
export const DefaultStory = (args) => {
return (
<div style={{ width: '600px' }}>
<CancelSpeedupPopover {...args} />
</div>
);
};
DefaultStory.storyName = 'Default';

Some files were not shown because too many files have changed in this diff Show More