Merge pull request #15746 from MetaMask/Version-v10.19.0

Version v10.19.0
feature/default_network_editable
Dan J Miller 2 years ago committed by GitHub
commit 0278de708d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 164
      .circleci/config.yml
  2. 68
      .circleci/scripts/bundle-stats-commit.sh
  3. 2
      .circleci/scripts/validate-lavamoat-policy.sh
  4. 6
      .depcheckrc.yml
  5. 9
      .eslintrc.base.js
  6. 22
      .eslintrc.js
  7. 1
      .github/CODEOWNERS
  8. 2
      .github/ISSUE_TEMPLATE/config.yml
  9. 4
      .gitignore
  10. 9
      .metamaskrc.dist
  11. 1
      .prettierignore
  12. 8
      .storybook/2.DOCUMENTATION.stories.mdx
  13. 10
      .storybook/3.COLORS.stories.mdx
  14. 307
      .storybook/4.SHADOW.stories.mdx
  15. 125
      .storybook/5.BREAKPOINTS.stories.mdx
  16. 2
      .storybook/metamask-storybook-theme.js
  17. 7
      .storybook/preview.js
  18. 428
      .storybook/test-data.js
  19. 70
      CHANGELOG.md
  20. 16
      README.md
  21. 98
      app/_locales/de/messages.json
  22. 154
      app/_locales/el/messages.json
  23. 660
      app/_locales/en/messages.json
  24. 156
      app/_locales/es/messages.json
  25. 22
      app/_locales/es_419/messages.json
  26. 506
      app/_locales/fr/messages.json
  27. 170
      app/_locales/hi/messages.json
  28. 154
      app/_locales/id/messages.json
  29. 3
      app/_locales/it/messages.json
  30. 154
      app/_locales/ja/messages.json
  31. 154
      app/_locales/ko/messages.json
  32. 2
      app/_locales/ph/messages.json
  33. 158
      app/_locales/pt/messages.json
  34. 22
      app/_locales/pt_BR/messages.json
  35. 152
      app/_locales/ru/messages.json
  36. 148
      app/_locales/tl/messages.json
  37. 154
      app/_locales/tr/messages.json
  38. 154
      app/_locales/vi/messages.json
  39. 117
      app/_locales/zh/messages.json
  40. 26
      app/_locales/zh_CN/messages.json
  41. BIN
      app/fonts/Euclid/EuclidCircularB-Medium.ttf
  42. 17
      app/images/eth_badge.svg
  43. 1
      app/manifest/v2/chrome.json
  44. 10
      app/manifest/v3/_base.json
  45. 3
      app/manifest/v3/chrome.json
  46. 133
      app/scripts/app-init.js
  47. 42
      app/scripts/background.js
  48. 16
      app/scripts/constants/contracts.js
  49. 118
      app/scripts/contentscript.js
  50. 32
      app/scripts/controllers/app-state.js
  51. 77
      app/scripts/controllers/backup.js
  52. 118
      app/scripts/controllers/backup.test.js
  53. 112
      app/scripts/controllers/detect-tokens.js
  54. 228
      app/scripts/controllers/detect-tokens.test.js
  55. 17
      app/scripts/controllers/ens/ens.js
  56. 2
      app/scripts/controllers/ens/index.test.js
  57. 6
      app/scripts/controllers/incoming-transactions.js
  58. 81
      app/scripts/controllers/incoming-transactions.test.js
  59. 77
      app/scripts/controllers/metametrics.js
  60. 11
      app/scripts/controllers/metametrics.test.js
  61. 17
      app/scripts/controllers/network/network-controller.test.js
  62. 3
      app/scripts/controllers/network/network.js
  63. 3
      app/scripts/controllers/network/pending-middleware.test.js
  64. 12
      app/scripts/controllers/network/util.test.js
  65. 5
      app/scripts/controllers/permissions/caveat-mutators.test.js
  66. 1
      app/scripts/controllers/permissions/flask/snap-permissions.test.js
  67. 7
      app/scripts/controllers/permissions/specifications.js
  68. 10
      app/scripts/controllers/permissions/specifications.test.js
  69. 10
      app/scripts/controllers/preferences.js
  70. 16
      app/scripts/controllers/preferences.test.js
  71. 31
      app/scripts/controllers/swaps.js
  72. 62
      app/scripts/controllers/swaps.test.js
  73. 5
      app/scripts/controllers/threebox.js
  74. 333
      app/scripts/controllers/transactions/index.js
  75. 105
      app/scripts/controllers/transactions/index.test.js
  76. 9
      app/scripts/controllers/transactions/pending-tx-tracker.js
  77. 39
      app/scripts/controllers/transactions/pending-tx-tracker.test.js
  78. 7
      app/scripts/controllers/transactions/tx-gas-utils.js
  79. 17
      app/scripts/controllers/transactions/tx-state-manager.js
  80. 2
      app/scripts/disable-console.js
  81. 3
      app/scripts/inpage.js
  82. 4
      app/scripts/lib/ComposableObservableStore.js
  83. 83
      app/scripts/lib/account-tracker.js
  84. 13
      app/scripts/lib/buy-url.js
  85. 5
      app/scripts/lib/buy-url.test.js
  86. 6
      app/scripts/lib/ens-ipfs/resolver.js
  87. 3
      app/scripts/lib/ens-ipfs/setup.js
  88. 10
      app/scripts/lib/metaRPCClientFactory.js
  89. 34
      app/scripts/lib/metaRPCClientFactory.test.js
  90. 3
      app/scripts/lib/network-store.js
  91. 11
      app/scripts/lib/personal-message-manager.js
  92. 4
      app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js
  93. 15
      app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js
  94. 9
      app/scripts/lib/setupSentry.js
  95. 5
      app/scripts/lib/typed-message-manager.js
  96. 241
      app/scripts/metamask-controller.actions.test.js
  97. 905
      app/scripts/metamask-controller.js
  98. 40
      app/scripts/metamask-controller.test.js
  99. 7
      app/scripts/migrations/022.test.js
  100. 3
      app/scripts/migrations/030.js
  101. Some files were not shown because too many files have changed in this diff Show More

@ -53,6 +53,9 @@ workflows:
- prep-build-test:
requires:
- prep-deps
- prep-build-test-mv3:
requires:
- prep-deps
- prep-build-test-flask:
requires:
- prep-deps
@ -62,6 +65,9 @@ workflows:
- prep-build-storybook:
requires:
- test-storybook
- prep-build-ts-migration-dashboard:
requires:
- prep-deps
- test-lint:
requires:
- prep-deps
@ -133,6 +139,12 @@ workflows:
- benchmark:
requires:
- prep-build-test
- user-actions-benchmark:
requires:
- prep-build-test
- stats-module-load-init:
requires:
- prep-build-test-mv3
- job-publish-prerelease:
requires:
- prep-deps
@ -140,7 +152,11 @@ workflows:
- prep-build-beta
- prep-build-flask
- prep-build-storybook
- prep-build-ts-migration-dashboard
- prep-build-test-mv3
- benchmark
- user-actions-benchmark
- stats-module-load-init
- all-tests-pass
- job-publish-release:
filters:
@ -157,6 +173,12 @@ workflows:
only: develop
requires:
- prep-build-storybook
- job-publish-ts-migration-dashboard:
filters:
branches:
only: develop
requires:
- prep-build-ts-migration-dashboard
jobs:
create_release_pull_request:
@ -224,9 +246,25 @@ jobs:
- checkout
- attach_workspace:
at: .
- when:
condition:
not:
matches:
pattern: /^master$/
value: << pipeline.git.branch >>
steps:
- run:
name: build:dist
command: yarn dist
command: yarn build dist
- when:
condition:
matches:
pattern: /^master$/
value: << pipeline.git.branch >>
steps:
- run:
name: build:prod
command: yarn build prod
- run:
name: build:debug
command: find dist/ -type f -exec md5sum {} \; | sort -k 2
@ -244,7 +282,7 @@ jobs:
at: .
- run:
name: build:dist
command: yarn build --build-type beta prod
command: yarn build --build-type beta dist
- run:
name: build:debug
command: find dist/ -type f -exec md5sum {} \; | sort -k 2
@ -268,7 +306,7 @@ jobs:
at: .
- run:
name: build:dist
command: yarn build --build-type flask prod
command: yarn build --build-type flask dist
- run:
name: build:debug
command: find dist/ -type f -exec md5sum {} \; | sort -k 2
@ -305,6 +343,26 @@ jobs:
- dist-test-flask
- builds-test-flask
prep-build-test-mv3:
executor: node-browsers-medium-plus
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Build extension in mv3 for testing
command: yarn build:test:mv3
- run:
name: Move test build to 'dist-test' to avoid conflict with production build
command: mv ./dist ./dist-test-mv3
- run:
name: Move test zips to 'builds-test' to avoid conflict with production build
command: mv ./builds ./builds-test-mv3
- persist_to_workspace:
root: .
paths:
- dist-test-mv3
- builds-test-mv3
prep-build-test:
executor: node-browsers-medium-plus
@ -341,6 +399,20 @@ jobs:
paths:
- storybook-build
prep-build-ts-migration-dashboard:
executor: node-browsers
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Build TypeScript migration dashboard
command: yarn ts-migration:dashboard:build
- persist_to_workspace:
root: .
paths:
- development/ts-migration-dashboard/build
test-storybook:
executor: node-browsers
steps:
@ -419,7 +491,6 @@ jobs:
name: Validate release candidate changelog
command: yarn lint:changelog:rc
test-deps-audit:
executor: node-browsers
steps:
@ -574,6 +645,69 @@ jobs:
paths:
- test-artifacts
user-actions-benchmark:
executor: node-browsers-medium-plus
steps:
- checkout
- run:
name: Re-Install Chrome
command: ./.circleci/scripts/chrome-install.sh
- attach_workspace:
at: .
- run:
name: Move test build to dist
command: mv ./dist-test ./dist
- run:
name: Move test zips to builds
command: mv ./builds-test ./builds
- run:
name: Run page load benchmark
command: yarn user-actions-benchmark:chrome --out test-artifacts/chrome/benchmark/user_actions.json --retries 2
- store_artifacts:
path: test-artifacts
destination: test-artifacts
- persist_to_workspace:
root: .
paths:
- test-artifacts
stats-module-load-init:
executor: node-browsers-medium-plus
steps:
- checkout
- run:
name: Re-Install Chrome
command: ./.circleci/scripts/chrome-install.sh
- attach_workspace:
at: .
- run:
name: Move test build to dist
command: mv ./dist-test-mv3 ./dist
- run:
name: Move test zips to builds
command: mv ./builds-test-mv3 ./builds
- run:
name: Run page load benchmark
command: |
mkdir -p test-artifacts/chrome/mv3
cp -R development/charts/flamegraph test-artifacts/chrome/mv3/initialisation
cp -R development/charts/flamegraph/chart test-artifacts/chrome/mv3/initialisation/background
cp -R development/charts/flamegraph/chart test-artifacts/chrome/mv3/initialisation/ui
cp -R development/charts/table test-artifacts/chrome/mv3/load_time
- run:
name: Run page load benchmark
command: yarn mv3:stats:chrome --out test-artifacts/chrome/mv3
- run:
name: Record bundle size at commit
command: ./.circleci/scripts/bundle-stats-commit.sh
- store_artifacts:
path: test-artifacts
destination: test-artifacts
- persist_to_workspace:
root: .
paths:
- test-artifacts
job-publish-prerelease:
executor: node-browsers
steps:
@ -622,6 +756,9 @@ jobs:
- store_artifacts:
path: storybook-build
destination: storybook
- store_artifacts:
path: development/ts-migration-dashboard/build
destination: ts-migration-dashboard
- run:
name: build:announce
command: ./development/metamaskbot-build-announce.js
@ -648,7 +785,7 @@ jobs:
steps:
- add_ssh_keys:
fingerprints:
- "3d:49:29:f4:b2:e8:ea:af:d1:32:eb:2a:fc:15:85:d8"
- '3d:49:29:f4:b2:e8:ea:af:d1:32:eb:2a:fc:15:85:d8'
- checkout
- attach_workspace:
at: .
@ -658,6 +795,23 @@ jobs:
git remote add storybook git@github.com:MetaMask/metamask-storybook.git
yarn storybook:deploy
job-publish-ts-migration-dashboard:
executor: node-browsers
steps:
- add_ssh_keys:
fingerprints:
- "8b:21:e3:20:7c:c9:db:82:74:2d:86:d6:11:a7:2f:49"
- checkout
- attach_workspace:
at: .
- run:
name: ts-migration-dashboard:deploy
command: |
git remote add ts-migration-dashboard git@github.com:MetaMask/metamask-extension-ts-migration-dashboard.git
git config user.name "MetaMask Bot"
git config user.email metamaskbot@users.noreply.github.com
yarn ts-migration:dashboard:deploy
test-unit:
executor: node-browsers
steps:

@ -0,0 +1,68 @@
#!/usr/bin/env bash
set -e
set -u
set -o pipefail
if [[ "${CI:-}" != 'true' ]]
then
printf '%s\n' 'CI environment variable must be set to true'
exit 1
fi
if [[ "${CIRCLECI:-}" != 'true' ]]
then
printf '%s\n' 'CIRCLECI environment variable must be set to true'
exit 1
fi
if [[ "${CIRCLE_BRANCH}" != "develop" ]]
then
printf 'This is not develop branch'
exit 0
fi
if [[ -z "${GITHUB_TOKEN:-}" ]]
then
printf '%s\n' 'GITHUB_TOKEN environment variable must be set'
exit 1
elif [[ -z "${GITHUB_TOKEN_USER:-}" ]]
then
printf '%s\n' 'GITHUB_TOKEN_USER environment variable must be set'
exit 1
fi
mkdir temp
git config --global user.email "metamaskbot@users.noreply.github.com"
git config --global user.name "MetaMask Bot"
git clone git@github.com:MetaMask/extension_bundlesize_stats.git temp
if [[ -f "temp/stats/bundle_size_stats-${CIRCLE_SHA1}.json" ]]
then
printf 'Bundle size of the commit is already recorded'
cd ..
rm -rf temp
exit 0
fi
cp -R test-artifacts/chrome/mv3/bundle_size_stats.json temp/stats
echo " bundle_size_stats-${CIRCLE_SHA1}.json" >> temp/stats/fileList.txt
mv temp/stats/bundle_size_stats.json "temp/stats/bundle_size_stats-${CIRCLE_SHA1}.json"
cd temp
git add .
git commit --message "Bundle size at commit: ${CIRCLE_SHA1}"
repo_slug="$CIRCLE_PROJECT_USERNAME/extension_bundlesize_stats"
git push "https://$GITHUB_TOKEN_USER:$GITHUB_TOKEN@github.com/$repo_slug" main
cd ..
rm -rf temp

@ -4,7 +4,7 @@ set -e
set -u
set -o pipefail
yarn lavamoat:auto
yarn lavamoat:auto:ci
if git diff --exit-code
then

@ -12,6 +12,8 @@ ignores:
# dev deps
#
# all @types/* packages are imported implicitly by TypeScript
- '@types/*'
# safety fallback for npm lifecycle scripts, not used normally
- '@lavamoat/preinstall-always-fail'
# used in testing + ci
@ -20,7 +22,7 @@ ignores:
- '@metamask/phishing-warning' # statically hosted as part of some e2e tests
- '@metamask/test-dapp'
- '@metamask/design-tokens' # Only imported in index.css
- '@tsconfig/node14' # required dynamically by TS, used in tsconfig.json
- '@tsconfig/node16' # required dynamically by TS, used in tsconfig.json
- '@sentry/cli' # invoked as `sentry-cli`
- 'chromedriver'
- 'depcheck' # ooo meta
@ -42,6 +44,8 @@ ignores:
- 'css-loader'
- 'sass-loader'
- 'resolve-url-loader'
# jest environments
- 'jest-environment-jsdom'
# files depcheck should not parse
ignorePatterns:

@ -63,5 +63,14 @@ module.exports = {
// a browser context. For instance, we may import polyfills which change
// global variables, or we may import stylesheets.
'import/no-unassigned-import': 'off',
// import/no-named-as-default-member checks if default imports also have
// named exports matching properties used on the default import. Example:
// in confirm-seed-phrase-component.test.js we import sinon from 'sinon'
// and later access sinon.spy. spy is also exported from sinon directly and
// thus triggers the error. Turning this rule off to prevent churn when
// upgrading eslint and dependencies. This rule should be evaluated and
// if agreeable turned on upstream in @metamask/eslint-config
'import/no-named-as-default-member': 'off',
},
};

@ -8,6 +8,8 @@ module.exports = {
'app/vendor/**',
'builds/**/*',
'development/chromereload.js',
'development/charts/**',
'development/ts-migration-dashboard/build/**',
'dist/**/*',
'node_modules/**/*',
],
@ -277,6 +279,20 @@ module.exports = {
{ maxSize: 50, inlineMaxSize: 50 },
],
'jest/no-restricted-matchers': 'off',
/**
* jest/prefer-to-be is a new rule that was disabled to reduce churn
* when upgrading eslint. It should be considered for use and enabled
* in a future PR if agreeable.
*/
'jest/prefer-to-be': 'off',
/**
* jest/lowercase-name was renamed to jest/prefer-lowercase-title this
* change was made to essentially retain the same state as the original
* eslint-config-jest until it is updated. At which point the following
* two lines can be deleted.
*/
'jest/lowercase-name': 'off',
'jest/prefer-lowercase-title': ['error', { ignore: ['describe'] }],
},
},
/**
@ -328,5 +344,11 @@ module.exports = {
sourceType: 'script',
},
},
{
files: ['ui/pages/settings/*.js'],
rules: {
'sort-keys': ['error', 'asc', { natural: true }],
},
},
],
};

@ -4,3 +4,4 @@
* @MetaMask/extension-devs
.circleci/ @MetaMask/extension-devs @kumavis
development/ @MetaMask/extension-devs @kumavis
lavamoat/ @MetaMask/supply-chain

@ -4,5 +4,5 @@ contact_links:
url: https://community.metamask.io/c/feature-requests-ideas/
about: Request new features and vote on the ones that are important to you
- name: Get support or ask a question
url: https://metamask.zendesk.com/hc/en-us/requests/new
url: https://metamask.zendesk.com/hc/en-us
about: Use the MetaMask support system to get help and ask questions

4
.gitignore vendored

@ -33,6 +33,8 @@ jest-coverage/
dist
builds/
builds.zip
development/ts-migration-dashboard/build
development/ts-migration-dashboard/intermediate
test-artifacts
test-builds
@ -45,7 +47,9 @@ notes.txt
.nyc_output
# MetaMask configuration
.metamaskrc
.metamaskprodrc
# TypeScript
tsout/

@ -5,8 +5,13 @@ SEGMENT_WRITE_KEY=
ONBOARDING_V2=
SWAPS_USE_DEV_APIS=
COLLECTIBLES_V1=
TOKEN_DETECTION_V2=
ADD_POPULAR_NETWORKS=
PUBNUB_PUB_KEY=
PUBNUB_SUB_KEY=
TOKEN_ALLOWANCE_IMPROVEMENTS=
PORTFOLIO_URL=
; Set this to '1' to enable support for Sign-In with Ethereum [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361)
SIWE_V1=
; Set this to test changes to the phishing warning page.
PHISHING_WARNING_PAGE_URL=

@ -11,3 +11,4 @@ app/vendor/**
test/e2e/send-eth-with-private-key-test/**
*.scss
development/chromereload.js
development/ts-migration-dashboard/filesToConvert.json

@ -50,7 +50,7 @@ import React from 'react';
import BuyIcon from '../icon/overview-buy-icon.component';
// The mdx file to document component API and usage
// The mdx file to document props and usage
import README from './README.mdx';
import Button from '.';
@ -149,7 +149,7 @@ Now the storybook components are complete, the `README.mdx` documentation should
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
<!-- import the component to use for the ArgsTable under ## Component API -->
<!-- import the component to use for the ArgsTable under ## Props -->
import Button from '.';
@ -167,9 +167,9 @@ Buttons communicate actions that users can take.
<Story id="ui-components-ui-button-button-stories-js--default-story" />
</Canvas>
## Component API
## Props
<!-- Display the component API using the ArgsTable. Use JSDoc style comments in the PropTypes of your component to add descriptions for props. See button.component.js Button.propTypes for an example of jsDoc style comments
<!-- Display the Props using the ArgsTable. Use JSDoc style comments in the PropTypes of your component to add descriptions for props. See button.component.js Button.propTypes for an example of jsDoc style comments
-->
<ArgsTable of={Button} />

@ -1,20 +1,12 @@
import { Meta } from '@storybook/addon-docs';
import ActionaleMessage from '../ui/components/ui/actionable-message';
import designTokenDiagramImage from './images/design.token.graphic.svg';
<Meta title="Design Tokens / Color" />
<Meta title="Foundations / Color" />
# Color
Color is used to express style and communicate meaning.
<ActionaleMessage
type="warning"
message="We are in the process of consolidating all of our colors, making them accessible and enabling theming. Many of the colors used throughout the codebase are deprecated please follow the guide below to ensure you are using the correct colors when building MetaMask UI"
/>
<br />
## Design tokens
We are importing design tokens as CSS variables from [@metamask/design-tokens](https://github.com/MetaMask/design-tokens) repo to help consolidate colors and enable theming across all MetaMask products.

@ -0,0 +1,307 @@
import { Meta, Canvas, Story } from '@storybook/addon-docs';
<Meta title="Foundations / Shadow" />
# Shadow
Shadows convey elevation of elements on a surface
## Size
There are 4 different sizes of shadow in MetaMask
<div
style={{
display: 'grid',
gap: '32px',
gridTemplateColumns: 'repeat(auto-fill, 200px)',
marginTop: 16,
marginBottom: 16,
}}
>
<div
style={{
height: 100,
backgroundColor: 'var(--color-background-default)',
boxShadow: 'var(--shadow-size-xs) var(--color-shadow-default',
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
}}
>
XS
</div>
<div
style={{
height: 100,
backgroundColor: 'var(--color-background-default)',
boxShadow: 'var(--shadow-size-sm) var(--color-shadow-default',
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
}}
>
SM
</div>
<div
style={{
height: 100,
backgroundColor: 'var(--color-background-default)',
boxShadow: 'var(--shadow-size-md) var(--color-shadow-default',
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
}}
>
MD
</div>
<div
style={{
height: 100,
backgroundColor: 'var(--color-background-default)',
boxShadow: 'var(--shadow-size-lg) var(--color-shadow-default',
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
}}
>
LG
</div>
</div>
| Size | CSS |
| ------ | ----------------------- |
| **XS** | `var(--shadow-size-xs)` |
| **SM** | `var(--shadow-size-sm)` |
| **MD** | `var(--shadow-size-md)` |
| **LG** | `var(--shadow-size-lg)` |
## Color
As well as the neutral colors for shadow 2 other colors exist that are used for the primary and error/danger button hover states
<div
style={{
display: 'grid',
gap: '32px',
gridTemplateColumns: 'repeat(auto-fill, 200px)',
marginTop: 16,
marginBottom: 16,
}}
>
<div
style={{
height: 100,
backgroundColor: 'var(--color-background-default)',
boxShadow: 'var(--shadow-size-lg) var(--color-shadow-default',
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
}}
color="default"
>
<span>Default</span>
</div>
<div
style={{
height: 100,
backgroundColor: 'var(--color-primary-default)',
boxShadow: 'var(--shadow-size-lg) var(--color-primary-shadow',
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
color: 'var(--color-primary-inverse)',
}}
>
<span>Primary</span>
</div>
<div
style={{
height: 100,
backgroundColor: 'var(--color-error-default)',
boxShadow: 'var(--shadow-size-lg) var(--color-error-shadow',
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
color: 'var(--color-error-inverse)',
}}
>
<span>Error/Danger</span>
</div>
</div>
| Color | CSS |
| ----------- | ----------------------------- |
| **neutral** | `var(--color-shadow-default)` |
| **primary** | `var(--color-primary-shadow)` |
| **danger** | `var(--color-error-shadow)` |
## Example usage
Using both size and color tokens, different shadows can be applied to components
<div>
<div
style={{
display: 'grid',
gap: '32px',
gridTemplateColumns: 'repeat(auto-fill, 200px)',
marginBottom: '64px',
}}
>
<div
style={{
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
height: 100,
textAlign: 'center',
boxShadow: 'var(--shadow-size-xs) var(--color-shadow-default)',
}}
>
<span>Card</span>
</div>
<div
style={{
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
height: 100,
textAlign: 'center',
boxShadow: 'var(--shadow-size-sm) var(--color-shadow-default)',
}}
>
<span>Dropdown</span>
</div>
<div
style={{
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
height: 100,
textAlign: 'center',
boxShadow: 'var(--shadow-size-md) var(--color-shadow-default)',
}}
>
<span>Toast</span>
</div>
<div
style={{
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
height: 100,
textAlign: 'center',
boxShadow: 'var(--shadow-size-lg) var(--color-shadow-default)',
}}
>
<span>Modal</span>
</div>
</div>
<div
style={{
display: 'grid',
gap: '32px',
gridTemplateColumns: 'repeat(auto-fill, 200px)',
}}
>
<div
style={{
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
height: 100,
textAlign: 'center',
boxShadow: 'var(--component-button-primary-shadow)',
backgroundColor: 'var(--color-primary-default)',
color: 'var(--color-primary-inverse)',
}}
>
<span>Button Primary Hover</span>
</div>
<div
style={{
borderRadius: '4px',
display: 'grid',
alignContent: 'center',
justifyContent: 'center',
spanAlign: 'center',
height: 100,
textAlign: 'center',
boxShadow: 'var(--component-button-danger-shadow)',
backgroundColor: 'var(--color-error-default)',
color: 'var(--color-error-inverse)',
}}
>
<span>Button Error/Danger Hover</span>
</div>
</div>
</div>
| Component | JS | CSS |
| ------------------------ | ---------------------------------------------------------------- | --- |
| **Card** | `box-shadow: var(--shadow-size-xs) var(--color-shadow-default);` |
| **Dropdown** | `box-shadow: var(--shadow-size-sm) var(--color-shadow-default);` |
| **Toast** | `box-shadow: var(--shadow-size-md) var(--color-shadow-default);` |
| **Modal** | `box-shadow: var(--shadow-size-lg) var(--color-shadow-default);` |
| **Button Primary Hover** | `box-shadow: var(--shadow-size-sm) var(--color-primary-shadow);` |
| **Button Danger Hover** | `box-shadow: var(--shadow-size-sm) var(--color-error-shadow);` |
## Takeaways
- Try to avoid using static media queries in your code
- Try to use the provided SCSS mixins
### ❌ Don't do this
Don't use static media queries in your code
```css
/**
* Don't do this
* Static box-shadows create inconsistency in elevation of elements
**/
.card {
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.2);
}
```
### ✅ Do this
Do use the provided shadow design token css variables
```css
.card {
box-shadow: var(--shadow-size-xs) var(--color-shadow-default);
}
```
## References
- [Shadow design tokens](https://metamask.github.io/design-tokens/?path=/docs/shadows-shadows--shadow)
- [Figma light theme colors library(shadows page)](https://www.figma.com/file/kdFzEC7xzSNw7cXteqgzDW/%5BColor%5D-Light-Theme?node-id=753%3A719)(internal use only)
- [Figma dark theme colors library(shadows page)](https://www.figma.com/file/rLKsoqpjyoKauYnFDcBIbO/%5BColor%5D-Dark-Theme?node-id=522%3A1022)(internal use only)

@ -0,0 +1,125 @@
import { Meta } from '@storybook/addon-docs';
<Meta title="Foundations / Breakpoints" />
# Breakpoints
Breakpoints are used for responsive layout
## Screen Sizes
There are 4 screen sizes that make up the breakpoints for the MetaMask extension
- base: `0px`
- sm: `576px`
- md: `768px`
- lg: `1280px`
### SCSS
There are Sass variables and mixins available for use for both min and max screens sizes
### Variables
```css
$screen-sm-max /* 575px */
$screen-md-max /* 767px */
$screen-lg-max /* 1279px */
$screen-sm-min /* 576px */
$screen-md-min /* 768px */
$screen-lg-min /* 1280px */
```
### Mixins
```css
/* Max screen size */
@include screen-sm-max {
/* equivalent css @media screen and (max-width: 575px) */
}
@include screen-md-max {
/* equivalent css @media screen and (max-width: 767px) */
}
@include screen-lg-max {
/* equivalent css @media screen and (max-width: 1279px) */
}
/* Min screen size */
@include screen-sm-min {
/* equivalent css @media screen and (min-width: 576px) */
}
@include screen-md-min {
/* equivalent css @media screen and (min-width: 768px) */
}
@include screen-lg-min {
/* equivalent css @media screen and (min-width: 1280px) */
}
```
Migrating from the old sass variables to the new mixins looks like this
```css
/* Max width */
/* Instead of the media query and sass variable */
@media screen and (max-width: $break-small) {
right: 16px;
}
/* Use the sass mixin */
@include screen-sm-max {
right: 16px;
}
/* Min width */
/* Instead of the media query and sass variable */
@media screen and (min-width: $break-large) {
left: 16px;
}
/* Use the sass mixin */
@include screen-sm-min {
left: 16px;
}
```
## Takeaways
- Try to avoid using static media queries in your code.
- Try to use the provided SCSS mixins
### ❌ Don't do this
Don't use static media queries in your code.
```css
/**
* Don't do this
* Static media queries create inconsistency and could break the UI if we want to update them in future
**/
.account-menu {
@media screen and (min-width: 769px) {
right: calc((100vw - 80vw) / 2);
}
@media screen and (min-width: 1281px) {
right: calc((100vw - 65vw) / 2);
}
}
```
### ✅ Do this
Do use the provided Sass mixins
```css
.account-menu {
@include screen-md-min {
right: calc((100vw - 80vw) / 2);
}
@include screen-lg-min {
right: calc((100vw - 65vw) / 2);
}
}
```

@ -7,6 +7,6 @@ export default create({
brandTitle: 'MetaMask Storybook',
// Typography
fontBase: 'Euclid, Roboto, Helvetica, Arial, sans-serif',
fontBase: 'Euclid Circular B, Roboto, Helvetica, Arial, sans-serif',
fontCode: 'Inconsolata, monospace',
});

@ -11,7 +11,7 @@ import MetaMetricsProviderStorybook from './metametrics';
import testData from './test-data.js';
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';
import { _setBackgroundConnection } from '../ui/store/actions';
import { _setBackgroundConnection } from '../ui/store/action-queue';
import MetaMaskStorybookTheme from './metamask-storybook-theme';
import addons from '@storybook/addons';
@ -30,9 +30,10 @@ addParameters({
storySort: {
order: [
'Getting Started',
'Design Tokens',
'Foundations',
['Color', 'Shadow', 'Breakpoints'],
'Components',
['UI', 'App'],
['UI', 'App', 'Component Library'],
'Pages',
],
},

@ -1,3 +1,4 @@
import { draftTransactionInitialState } from '../ui/ducks/send';
const state = {
invalidCustomNetwork: {
state: 'CLOSED',
@ -15,99 +16,110 @@ const state = {
},
networkList: [
{
blockExplorerUrl: "https://etherscan.io",
chainId: "0x1",
blockExplorerUrl: 'https://etherscan.io',
chainId: '0x1',
iconColor: 'var(--mainnet)',
isATestNetwork: false,
labelKey: "mainnet",
providerType: "mainnet",
rpcUrl: "https://mainnet.infura.io/v3/",
ticker: "ETH",
labelKey: 'mainnet',
providerType: 'mainnet',
rpcUrl: 'https://mainnet.infura.io/v3/',
ticker: 'ETH',
viewOnly: true,
},
{
blockExplorerUrl: "https://ropsten.etherscan.io",
chainId: "0x3",
blockExplorerUrl: 'https://ropsten.etherscan.io',
chainId: '0x3',
iconColor: 'var(--ropsten)',
isATestNetwork: true,
labelKey: "ropsten",
providerType: "ropsten",
rpcUrl: "https://ropsten.infura.io/v3/",
ticker: "ETH",
labelKey: 'ropsten',
providerType: 'ropsten',
rpcUrl: 'https://ropsten.infura.io/v3/',
ticker: 'ETH',
viewOnly: true,
},
{
blockExplorerUrl: "https://rinkeby.etherscan.io",
chainId: "0x4",
blockExplorerUrl: 'https://rinkeby.etherscan.io',
chainId: '0x4',
iconColor: 'var(--rinkeby)',
isATestNetwork: true,
labelKey: "rinkeby",
providerType: "rinkeby",
rpcUrl: "https://rinkeby.infura.io/v3/",
ticker: "ETH",
labelKey: 'rinkeby',
providerType: 'rinkeby',
rpcUrl: 'https://rinkeby.infura.io/v3/',
ticker: 'ETH',
viewOnly: true,
},
{
blockExplorerUrl: "https://goerli.etherscan.io",
chainId: "0x5",
blockExplorerUrl: 'https://goerli.etherscan.io',
chainId: '0x5',
iconColor: 'var(--goerli)',
isATestNetwork: true,
labelKey: "goerli",
providerType: "goerli",
rpcUrl: "https://goerli.infura.io/v3/",
ticker: "ETH",
labelKey: 'goerli',
providerType: 'goerli',
rpcUrl: 'https://goerli.infura.io/v3/',
ticker: 'ETH',
viewOnly: true,
},
{
blockExplorerUrl: "https://kovan.etherscan.io",
chainId: "0x2a",
blockExplorerUrl: 'https://kovan.etherscan.io',
chainId: '0x2a',
iconColor: 'var(--kovan)',
isATestNetwork: true,
labelKey: "kovan",
providerType: "kovan",
rpcUrl: "https://kovan.infura.io/v3/",
ticker: "ETH",
labelKey: 'kovan',
providerType: 'kovan',
rpcUrl: 'https://kovan.infura.io/v3/',
ticker: 'ETH',
viewOnly: true,
},
{
blockExplorerUrl: 'https://sepolia.etherscan.io',
chainId: '0xaa36a7',
iconColor: 'var(--sepolia)',
isATestNetwork: true,
labelKey: 'sepolia',
providerType: 'sepolia',
rpcUrl: 'https://sepolia.infura.io/v3/',
ticker: 'ETH',
viewOnly: true,
},
{
blockExplorerUrl: "",
chainId: "0x539",
blockExplorerUrl: '',
chainId: '0x539',
iconColor: 'var(--localhost)',
isATestNetwork: true,
label: "Localhost 8545",
providerType: "rpc",
rpcUrl: "http://localhost:8545",
ticker: "ETH",
label: 'Localhost 8545',
providerType: 'rpc',
rpcUrl: 'http://localhost:8545',
ticker: 'ETH',
},
{
blockExplorerUrl: "https://bscscan.com",
chainId: "0x38",
blockExplorerUrl: 'https://bscscan.com',
chainId: '0x38',
iconColor: 'var(--localhost)',
isATestNetwork: false,
label: "Binance Smart Chain",
providerType: "rpc",
rpcUrl: "https://bsc-dataseed.binance.org/",
ticker: "BNB",
label: 'Binance Smart Chain',
providerType: 'rpc',
rpcUrl: 'https://bsc-dataseed.binance.org/',
ticker: 'BNB',
},
{
blockExplorerUrl: "https://cchain.explorer.avax.network/",
chainId: "0xa86a",
blockExplorerUrl: 'https://cchain.explorer.avax.network/',
chainId: '0xa86a',
iconColor: 'var(--localhost)',
isATestNetwork: false,
label: "Avalanche",
providerType: "rpc",
rpcUrl: "https://api.avax.network/ext/bc/C/rpc",
ticker: "AVAX",
label: 'Avalanche',
providerType: 'rpc',
rpcUrl: 'https://api.avax.network/ext/bc/C/rpc',
ticker: 'AVAX',
},
{
blockExplorerUrl: "https://polygonscan.com",
chainId: "0x89",
blockExplorerUrl: 'https://polygonscan.com',
chainId: '0x89',
iconColor: 'var(--localhost)',
isATestNetwork: false,
label: "Polygon",
providerType: "rpc",
rpcUrl: "https://polygon-rpc.com",
ticker: "MATIC",
label: 'Polygon',
providerType: 'rpc',
rpcUrl: 'https://polygon-rpc.com',
ticker: 'MATIC',
},
],
metamask: {
@ -132,7 +144,7 @@ const state = {
'0x',
],
occurrences: 12,
unlisted: false
unlisted: false,
},
'0xc00e94cb662c3520282e6f5717214004a7f26888': {
address: '0xc00e94cb662c3520282e6f5717214004a7f26888',
@ -153,23 +165,18 @@ const state = {
'0x',
],
occurrences: 12,
unlisted: false
unlisted: false,
},
'0xfffffffff15abf397da76f1dcc1a1604f45126db': {
address: '0xfffffffff15abf397da76f1dcc1a1604f45126db',
symbol: 'FSW',
decimals: 18,
name: 'Falconswap',
iconUrl: 'https://assets.coingecko.com/coins/images/12256/thumb/falconswap.png?1598534184',
aggregators: [
'CoinGecko',
'1inch',
'Paraswap',
'Zapper',
'Zerion',
],
occurrences: 12,
unlisted: false
iconUrl:
'https://assets.coingecko.com/coins/images/12256/thumb/falconswap.png?1598534184',
aggregators: ['CoinGecko', '1inch', 'Lifi'],
occurrences:3,
unlisted: false,
},
'0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f': {
address: '0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f',
@ -192,70 +199,70 @@ const state = {
'0x',
],
occurrences: 12,
unlisted: false
unlisted: false,
},
'0x6b175474e89094c44da98b954eedeac495271d0f': {
address: '0x6b175474e89094c44da98b954eedeac495271d0f',
symbol: 'META',
decimals: 18,
image: 'metamark.svg',
unlisted: false
unlisted: false,
},
'0xB8c77482e45F1F44dE1745F52C74426C631bDD52': {
address: '0xB8c77482e45F1F44dE1745F52C74426C631bDD52',
symbol: '0X',
decimals: 18,
image: '0x.svg',
unlisted: false
unlisted: false,
},
'0x1f9840a85d5af5bf1d1762f925bdaddc4201f984': {
address: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
symbol: 'AST',
decimals: 18,
image: 'ast.png',
unlisted: false
unlisted: false,
},
'0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2': {
address: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2',
symbol: 'BAT',
decimals: 18,
image: 'BAT_icon.svg',
unlisted: false
unlisted: false,
},
'0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1': {
address: '0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1',
symbol: 'CVL',
decimals: 18,
image: 'CVL_token.svg',
unlisted: false
unlisted: false,
},
'0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e': {
address: '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e',
symbol: 'GLA',
decimals: 18,
image: 'gladius.svg',
unlisted: false
unlisted: false,
},
'0x467Bccd9d29f223BcE8043b84E8C8B282827790F': {
address: '0x467Bccd9d29f223BcE8043b84E8C8B282827790F',
symbol: 'GNO',
decimals: 18,
image: 'gnosis.svg',
unlisted: false
unlisted: false,
},
'0xff20817765cb7f73d4bde2e66e067e58d11095c2': {
address: '0xff20817765cb7f73d4bde2e66e067e58d11095c2',
symbol: 'OMG',
decimals: 18,
image: 'omg.jpg',
unlisted: false
unlisted: false,
},
'0x8e870d67f660d95d5be530380d0ec0bd388289e1': {
address: '0x8e870d67f660d95d5be530380d0ec0bd388289e1',
symbol: 'WED',
decimals: 18,
image: 'wed.png',
unlisted: false
unlisted: false,
},
},
networkDetails: {
@ -281,83 +288,84 @@ const state = {
swapsFeatureIsLive: false,
swapsQuoteRefreshTime: 60000,
},
"snapStates": {},
"snaps": {
"local:http://localhost:8080/": {
"enabled": true,
"id": "local:http://localhost:8080/",
"initialPermissions": {
"snap_confirm": {}
},
"manifest": {
"description": "An example MetaMask Snap.",
"initialPermissions": {
"snap_confirm": {}
},
"manifestVersion": "0.1",
"proposedName": "MetaMask Example Snap",
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/snaps-skunkworks.git"
},
"source": {
"location": {
"npm": {
"filePath": "dist/bundle.js",
"iconPath": "images/icon.svg",
"packageName": "@metamask/example-snap",
"registry": "https://registry.npmjs.org/"
}
},
"shasum": "3lEt0yUu080DwV78neROaAAIQWXukSkMnP4OBhOhBnE="
},
"version": "0.6.0"
},
"permissionName": "wallet_snap_local:http://localhost:8080/",
"sourceCode": "(...)",
"status": "stopped",
"svgIcon": "<svg>...</svg>",
"version": "0.6.0"
},
"Filecoin Snap": {
"enabled": true,
"id": "npm:http://localhost:8080/",
"initialPermissions": {
"snap_confirm": {},
"eth_accounts": {},
"snap_manageState": {},
},
"manifest": {
"description": "This swap provides developers everywhere access to an entirely new data storage paradigm, even letting your programs store data autonomously. Learn more.",
"initialPermissions": {
"snap_confirm": {},
"eth_accounts": {},
"snap_manageState": {},
},
"manifestVersion": "0.1",
"proposedName": "Filecoin Snap",
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/snaps-skunkworks.git"
},
"source": {
"location": {
"npm": {
"filePath": "dist/bundle.js",
"iconPath": "images/icon.svg",
"packageName": "@metamask/example-snap",
"registry": "https://registry.npmjs.org/"
}
},
"shasum": "3lEt0yUu080DwV78neROaAAIQWXukSkMnP4OBhOhBnE="
},
"version": "0.6.0"
},
"permissionName": "wallet_snap_npm:http://localhost:8080/",
"sourceCode": "(...)",
"status": "stopped",
"svgIcon": "<svg>...</svg>",
"version": "0.6.0"
snapStates: {},
snaps: {
'local:http://localhost:8080/': {
enabled: true,
id: 'local:http://localhost:8080/',
initialPermissions: {
snap_confirm: {},
},
manifest: {
description: 'An example MetaMask Snap.',
initialPermissions: {
snap_confirm: {},
},
manifestVersion: '0.1',
proposedName: 'MetaMask Example Snap',
repository: {
type: 'git',
url: 'https://github.com/MetaMask/snaps-skunkworks.git',
},
source: {
location: {
npm: {
filePath: 'dist/bundle.js',
iconPath: 'images/icon.svg',
packageName: '@metamask/example-snap',
registry: 'https://registry.npmjs.org/',
},
},
shasum: '3lEt0yUu080DwV78neROaAAIQWXukSkMnP4OBhOhBnE=',
},
version: '0.6.0',
},
permissionName: 'wallet_snap_local:http://localhost:8080/',
sourceCode: '(...)',
status: 'stopped',
svgIcon: '<svg>...</svg>',
version: '0.6.0',
},
'Filecoin Snap': {
enabled: true,
id: 'npm:http://localhost:8080/',
initialPermissions: {
snap_confirm: {},
eth_accounts: {},
snap_manageState: {},
},
manifest: {
description:
'This swap provides developers everywhere access to an entirely new data storage paradigm, even letting your programs store data autonomously. Learn more.',
initialPermissions: {
snap_confirm: {},
eth_accounts: {},
snap_manageState: {},
},
manifestVersion: '0.1',
proposedName: 'Filecoin Snap',
repository: {
type: 'git',
url: 'https://github.com/MetaMask/snaps-skunkworks.git',
},
source: {
location: {
npm: {
filePath: 'dist/bundle.js',
iconPath: 'images/icon.svg',
packageName: '@metamask/example-snap',
registry: 'https://registry.npmjs.org/',
},
},
shasum: '3lEt0yUu080DwV78neROaAAIQWXukSkMnP4OBhOhBnE=',
},
version: '0.6.0',
},
permissionName: 'wallet_snap_npm:http://localhost:8080/',
sourceCode: '(...)',
status: 'stopped',
svgIcon: '<svg>...</svg>',
version: '0.6.0',
},
},
accountArray: [
@ -486,32 +494,46 @@ const state = {
],
detectedTokens: [
{
address: "0x514910771AF9Ca656af840dff83E8264EcF986CA",
address: '0x514910771AF9Ca656af840dff83E8264EcF986CA',
decimals: 18,
symbol: "LINK",
image: "https://crypto.com/price/coin-data/icon/LINK/color_icon.png",
aggregators:[
"coinGecko","oneInch","paraswap","zapper","zerion"
]
symbol: 'LINK',
image: 'https://crypto.com/price/coin-data/icon/LINK/color_icon.png',
aggregators: ['coinGecko', 'oneInch', 'paraswap', 'zapper', 'zerion'],
},
{
address: "0xc00e94Cb662C3520282E6f5717214004A7f26888",
address: '0xc00e94Cb662C3520282E6f5717214004A7f26888',
decimals: 18,
symbol: "COMP",
image: "https://crypto.com/price/coin-data/icon/COMP/color_icon.png",
symbol: 'COMP',
image: 'https://crypto.com/price/coin-data/icon/COMP/color_icon.png',
aggregators: [
"bancor","cmc","cryptocom","coinGecko","oneInch","paraswap","pmm","zapper","zerion","zeroEx"
]
'bancor',
'cmc',
'cryptocom',
'coinGecko',
'oneInch',
'paraswap',
'pmm',
'zapper',
'zerion',
'zeroEx',
],
},
{
address: "0xfffffffFf15AbF397dA76f1dcc1A1604F45126DB",
address: '0xfffffffFf15AbF397dA76f1dcc1A1604F45126DB',
decimals: 18,
symbol: "FSW",
image: "https://assets.coingecko.com/coins/images/12256/thumb/falconswap.png?1598534184",
symbol: 'FSW',
image:
'https://assets.coingecko.com/coins/images/12256/thumb/falconswap.png?1598534184',
aggregators: [
"aave", "cmc","coinGecko","oneInch","paraswap","zapper","zerion"
]
}
'aave',
'cmc',
'coinGecko',
'oneInch',
'paraswap',
'zapper',
'zerion',
],
},
],
pendingTokens: {},
customNonceValue: '',
@ -559,18 +581,18 @@ const state = {
swapsWelcomeMessageHasBeenShown: true,
defaultHomeActiveTabName: 'Assets',
provider: {
type: 'ropsten',
type: 'rinkeby',
ticker: 'ETH',
nickname: '',
rpcUrl: '',
chainId: '0x3',
chainId: '0x4',
},
previousProviderStore: {
type: 'ropsten',
type: 'rinkeby',
ticker: 'ETH',
nickname: '',
rpcUrl: '',
chainId: '0x3',
chainId: '0x4',
},
network: '3',
accounts: {
@ -1299,12 +1321,13 @@ const state = {
rinkeby: null,
kovan: null,
goerli: null,
sepolia: null,
mainnet: 10902989,
},
subjects: {
'https://app.uniswap.org': {
permissions: {
'eth_accounts': {
eth_accounts: {
invoker: 'https://app.uniswap.org',
parentCapability: 'eth_accounts',
id: 'a7342e4b-beae-4525-a36c-c0635fd03359',
@ -1318,14 +1341,14 @@ const state = {
},
},
},
"local:http://localhost:8080/": {
'local:http://localhost:8080/': {
permissions: {
'snap_confirm': {
invoker: "local:http://localhost:8080/",
snap_confirm: {
invoker: 'local:http://localhost:8080/',
parentCapability: 'snap_confirm',
id: 'a7342F4b-beae-4525-a36c-c0635fd03359',
date: 1620710693178,
caveats: []
caveats: [],
},
},
},
@ -1423,30 +1446,30 @@ const state = {
pendingApprovals: {},
pendingApprovalCount: 0,
subjectMetadata: {
"http://localhost:8080": {
'http://localhost:8080': {
extensionId: null,
iconUrl: null,
name: "Hello, Snaps!",
origin: "http://localhost:8080",
subjectType: "website"
name: 'Hello, Snaps!',
origin: 'http://localhost:8080',
subjectType: 'website',
},
"https://metamask.github.io": {
'https://metamask.github.io': {
extensionId: null,
iconUrl: null,
name: "Snaps Iframe Execution Environment",
origin: "https://metamask.github.io",
subjectType: "website"
name: 'Snaps Iframe Execution Environment',
origin: 'https://metamask.github.io',
subjectType: 'website',
},
"local:http://localhost:8080/": {
'local:http://localhost:8080/': {
extensionId: null,
iconUrl: null,
name: "MetaMask Example Snap",
origin: "local:http://localhost:8080/",
subjectType: "snap",
svgIcon: "<svg>...</svg>",
version: "0.6.0"
}
}
name: 'MetaMask Example Snap',
origin: 'local:http://localhost:8080/',
subjectType: 'snap',
svgIcon: '<svg>...</svg>',
version: '0.6.0',
},
},
},
appState: {
shouldClose: false,
@ -1455,7 +1478,14 @@ const state = {
open: false,
modalState: {
name: null,
props: {},
props: {
token: {
address: '0xaD6D458402F60fD3Bd25163575031ACDce07538D',
symbol: 'DAI',
decimals: 18,
},
history: {},
},
},
previousModalState: {
name: null,
@ -1507,6 +1537,12 @@ const state = {
amount: {
error: 'amount',
},
currentTransactionUUID: 'test-uuid',
draftTransactions: {
'test-uuid': {
...draftTransactionInitialState,
},
},
},
confirmTransaction: {
txData: {

@ -6,6 +6,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [10.19.0]
### Added
- Add ENS wildcard and secure offchain resolution (ENSIP-10 & EIP3668) ([#14675](https://github.com/MetaMask/metamask-extension/pull/14675))
- Add "What's New" notification about the Merge ([#15846](https://github.com/MetaMask/metamask-extension/pull/15846))
- Add "What's New" notification about 3box deprecation ([#15763](https://github.com/MetaMask/metamask-extension/pull/15763))
- Add "What's New" notification for Add Popular Networks feature ([#15121](https://github.com/MetaMask/metamask-extension/pull/15121))
- Add "What's New" notification for Token Detection feature ([#15807](https://github.com/MetaMask/metamask-extension/pull/15807))
- Add Warning for Rinkeby, Ropsten and Kovan test networks deprecation ([#15725](https://github.com/MetaMask/metamask-extension/pull/15725))
- Add Network Info popup when changing selected Network ([#13319](https://github.com/MetaMask/metamask-extension/pull/13319))
- Add Goerli, BSC, Optimism, Polygon, Avalance, Fantom and Arbitrum networks to query balances using BalanceChecker ([#13846]
- Add Token detection V2 ([#15138](https://github.com/MetaMask/metamask-extension/pull/15138))
- Add Backup and Restore State functionalities for 3box replacement ([#15243](https://github.com/MetaMask/metamask-extension/pull/15243))(https://github.com/MetaMask/metamask-extension/pull/13846))
- Add a Warning on the Confirmation screen, when user is performing a SetApprovalForAll transaction ([#15512](https://github.com/MetaMask/metamask-extension/pull/15512))
- Add new translations for DE, EL, ES, FR, HI, ID, JA, KO, PT, RU, TL TR, VI and ZH ([#14975](https://github.com/MetaMask/metamask-extension/pull/14975))
- Add Italian translation for "Forgot password" message ([#15477](https://github.com/MetaMask/metamask-extension/pull/15477))
- Add link to Metalabs dapp into MetaMask home page ([#15407](https://github.com/MetaMask/metamask-extension/pull/15407))
- Add Sepolia as a default test network ([#15787](https://github.com/MetaMask/metamask-extension/pull/15787))
- Swaps: on the STX status page ([#14995](https://github.com/MetaMask/metamask-extension/pull/14995))
- Add a link to the Swap tx
- Add a link for "Create a new swap"
- Swaps: add a tx details view for cancelled Swaps on the Activity tab ([#15273](https://github.com/MetaMask/metamask-extension/pull/15273))
- [FLASK] Add snap update metrics ([#15206](https://github.com/MetaMask/metamask-extension/pull/15206))
- [FLASK] Add `snap_getBip44Entropy` RPC method and deprecate `snap_getBip44Entropy_*` ([#15706](https://github.com/MetaMask/metamask-extension/pull/15706))
- [FLASK] Add `snap_getBip32Entropy` JSON-RPC method ([#15619](https://github.com/MetaMask/metamask-extension/pull/15619))
- [FLASK] Add DApp triggered Snap updates ([#15143](https://github.com/MetaMask/metamask-extension/pull/15143))
### Changed
- Update Confirmation screen styling by adding a top margin and moving Site Icon next to the site URL ([#15136](https://github.com/MetaMask/metamask-extension/pull/15136))
- Remove 'ADD_POPULAR_NETWORKS' feature flag as the feature is enabled by default ([#15229](https://github.com/MetaMask/metamask-extension/pull/15229))
- Remove dark mode and transaction insights from the "What's New" announcements ([#15279](https://github.com/MetaMask/metamask-extension/pull/15279))
- Update design tokens package and consolidate shadow values with new tokens ([#15264](https://github.com/MetaMask/metamask-extension/pull/15264))
- Update Zendesk ticket request URLs for a facilitating the bug reporting process ([#15458](https://github.com/MetaMask/metamask-extension/pull/15458))
- Update Customize Nonce 'Learn More' link pointing to the correct Zendesk article ([#15658](https://github.com/MetaMask/metamask-extension/pull/15658))
- Update Avalanche RPC endpoint to point Infura, as it is now supported ([#15720](https://github.com/MetaMask/metamask-extension/pull/15720))
- Update the main text on SetApprovalForAll confirmation screen ([#15724](https://github.com/MetaMask/metamask-extension/pull/15724))
- Updated origin pill component to match the new design for Permissions and Switching Networks screens ([#15603](https://github.com/MetaMask/metamask-extension/pull/15603))
- Set "View full transaction details" as default option on SetApprovalForAll Confirmation screen ([#15726](https://github.com/MetaMask/metamask-extension/pull/15726))
- Update `gridplus-sdk` ([#15711](https://github.com/MetaMask/metamask-extension/pull/15711))
- `eth-lattice-keyring` changes: [GridPlus/eth-lattice-keyring@v0.11.0...v0.12.0](https://github.com/GridPlus/eth-lattice-keyring/compare/v0.11.0...v0.12.0)
- `gridplus-sdk` changes: [GridPlus/gridplus-sdk@v2.2.2...v2.2.7](https://github.com/GridPlus/gridplus-sdk/compare/v2.2.2...v2.2.7)
- Update title case to sentence case with some exceptions (names, companies, special terms and page navigation) ([#15285](https://github.com/MetaMask/metamask-extension/pull/15285))
- Swaps: show a block explorer URL and hide "Add token" prompt, if block explorer URL is not available on Build Quote screen ([#15198](https://github.com/MetaMask/metamask-extension/pull/15198))
- Swaps: Standardize the spelling of 'cancelled' ([#15266](https://github.com/MetaMask/metamask-extension/pull/15266))
- Swaps: Performance improvements for Swaps, especially on the Build Quote page (~7.2x faster) ([#15359](https://github.com/MetaMask/metamask-extension/pull/15359))
- Swaps: only check if a user has enough balance before calling STX. If "Not enough funds" error is thrown, show a notification, but keep STX active ([#15218](https://github.com/MetaMask/metamask-extension/pull/15218))
- [FLASK] Disable "Mark all as read button" when there are no notifications ([#15333](https://github.com/MetaMask/metamask-extension/pull/15333))
### Fixed
- Fix Amount set to 0 on Send page when editing an ERC20 tx by preserving its value ([#15275](https://github.com/MetaMask/metamask-extension/pull/15275))
- Fix limited scroll area in the Account Menu by adjusting the height of Account Menu nav items, expanding scroll area ([#15302](https://github.com/MetaMask/metamask-extension/pull/15302))
- Fix Actionable Message overlapping tabs on Confirm Transaction view ([#15272](https://github.com/MetaMask/metamask-extension/pull/15272))
- Fix Contacts breadcrumb when viewing Contact Details ([#15663](https://github.com/MetaMask/metamask-extension/pull/15663))
- Fix Blockies identicon alignment in General Settings ([#15652](https://github.com/MetaMask/metamask-extension/pull/15652))
- Fix extra space and period in Custom Token warning text ([#15650](https://github.com/MetaMask/metamask-extension/pull/15650))
- Fix Signature Request styles: overlapping text when using hardware wallet and icon shrinking with long URLs ([#15621](https://github.com/MetaMask/metamask-extension/pull/15621))
- Fix for randomly resets of updated values in network edit form ([#14370](https://github.com/MetaMask/metamask-extension/pull/14370))
- Fix Connected site popup styling, by aligning the rows, shrinking the site icon with long URLs and preventing size change on Disconnect button hover ([#15409](https://github.com/MetaMask/metamask-extension/pull/15409))
- Fix Blockies icon on Recipient Details, by ensuring it is used when the option is enabled ([#15662](https://github.com/MetaMask/metamask-extension/pull/15662))
- Fix casing in Import Token for Spanish locale ([#15687](https://github.com/MetaMask/metamask-extension/pull/15687))
- Fix approval warning condition for `setApprovalForAll` so it is not shown when revoking ([#15806](https://github.com/MetaMask/metamask-extension/pull/15806))
- Fix disabled Sign button on the Sign Message screen unless scrolled to the bottom ([#15788](https://github.com/MetaMask/metamask-extension/pull/15788))
- Swaps: fix displaying "No tokens available matching" when there are tokens available, by updating the `results` state if `initialResultsState` array length is different ([#15270](https://github.com/MetaMask/metamask-extension/pull/15270))
- Swaps: fix redirect button to the Activity tab, after performing a Swap and clicking "View in Activity" [(#15620)](https://github.com/MetaMask/metamask-extension/pull/15620))
- [FLASK] Fix missing snap hook for `getAppKey`, the `getUnlockPromise` hook was missing, breaking some snap examples ([#15354](https://github.com/MetaMask/metamask-extension/pull/15354))
- [FLASK] Fix snaps authorship pill truncation, previously the authorship label would not be truncated and offset the UI ([#15190](https://github.com/MetaMask/metamask-extension/pull/15190))
- [FLASK] Fix Snaps key management permission copy ([#14849](https://github.com/MetaMask/metamask-extension/pull/14849))
## [10.18.4]
### Changed
- Update `eth-lattice-keyring` to v0.10.0 which itself updates `gridplus-sdk` ([#15261](https://github.com/MetaMask/metamask-extension/pull/15261))
@ -3122,7 +3189,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Uncategorized
- Added the ability to restore accounts from seed words.
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.18.4...HEAD
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.19.0...HEAD
[10.19.0]: https://github.com/MetaMask/metamask-extension/compare/v10.18.4...v10.19.0
[10.18.4]: https://github.com/MetaMask/metamask-extension/compare/v10.18.3...v10.18.4
[10.18.3]: https://github.com/MetaMask/metamask-extension/compare/v10.18.2...v10.18.3
[10.18.2]: https://github.com/MetaMask/metamask-extension/compare/v10.18.1...v10.18.2

@ -14,7 +14,7 @@ To learn how to contribute to the MetaMask project itself, visit our [Internal D
## Building locally
- Install [Node.js](https://nodejs.org) version 14
- Install [Node.js](https://nodejs.org) version 16
- If you are using [nvm](https://github.com/creationix/nvm#installation) (recommended) running `nvm use` will automatically choose the right node version for you.
- Install [Yarn](https://yarnpkg.com/en/docs/install)
- Install dependencies: `yarn setup` (not the usual install command)
@ -91,20 +91,25 @@ Whenever you change dependencies (adding, removing, or updating, either in `pack
* The `allow-scripts` configuration in `package.json`
* Run `yarn allow-scripts auto` to update the `allow-scripts` configuration automatically. This config determines whether the package's install/postinstall scripts are allowed to run. Review each new package to determine whether the install script needs to run or not, testing if necessary.
* Unfortunately, `yarn allow-scripts auto` will behave inconsistently on different platforms. macOS and Windows users may see extraneous changes relating to optional dependencies.
* The LavaMoat policy files. The _tl;dr_ is to run `yarn lavamoat:auto` to update these files, but there can be devils in the details. Continue reading for more information.
* The LavaMoat policy files. The _tl;dr_ is to run `yarn lavamoat:auto` to update these files, but there can be devils in the details:
* There are two sets of LavaMoat policy files:
* The production LavaMoat policy files (`lavamoat/browserify/*/policy.json`), which are re-generated using `yarn lavamoat:background:auto`.
* The production LavaMoat policy files (`lavamoat/browserify/*/policy.json`), which are re-generated using `yarn lavamoat:background:auto`. Add `--help` for usage.
* These should be regenerated whenever the production dependencies for the background change.
* The build system LavaMoat policy file (`lavamoat/build-system/policy.json`), which is re-generated using `yarn lavamoat:build:auto`.
* This should be regenerated whenever the dependencies used by the build system itself change.
* Whenever you regenerate a policy file, review the changes to determine whether the access granted to each package seems appropriate.
* Unfortunately, `yarn lavamoat:auto` will behave inconsistently on different platforms.
macOS and Windows users may see extraneous changes relating to optional dependencies.
* If you keep getting policy failures even after regenerating the policy files, try regenerating the policies after a clean install by doing:
* `rm -rf node_modules/ && yarn setup && yarn lavamoat:auto`
* Keep in mind that any kind of dynamic import or dynamic use of globals may elude LavaMoat's static analysis.
Refer to the LavaMoat documentation or ask for help if you run into any issues.
## Architecture
- [Visual of the controller heirarchy and dependencies as of summer 2022.](https://gist.github.com/rekmarks/8dba6306695dcd44967cce4b6a94ae33)
- [Visual of the entire codebase.](https://mango-dune-07a8b7110.1.azurestaticapps.net/?repo=metamask%2Fmetamask-extension)
[![Architecture Diagram](./docs/architecture.png)][1]
## Other Docs
@ -117,4 +122,9 @@ Whenever you change dependencies (adding, removing, or updating, either in `pack
- [Developing on MetaMask](./development/README.md)
- [How to generate a visualization of this repository's development](./development/gource-viz.sh)
## Dapp Developer Resources
- [Extend MetaMask's features w/ MetaMask Snaps.](https://docs.metamask.io/guide/snaps.html)
- [Prompt your users to add and switch to a new network.](https://medium.com/metamask/connect-users-to-layer-2-networks-with-the-metamask-custom-networks-api-d0873fac51e5)
- [Change the logo that appears when your dapp connects to MetaMask.](https://docs.metamask.io/guide/defining-your-icon.html)
[1]: http://www.nomnoml.com/#view/%5B%3Cactor%3Euser%5D%0A%0A%5Bmetamask-ui%7C%0A%20%20%20%5Btools%7C%0A%20%20%20%20%20react%0A%20%20%20%20%20redux%0A%20%20%20%20%20thunk%0A%20%20%20%20%20ethUtils%0A%20%20%20%20%20jazzicon%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20account-detail%0A%20%20%20%20%20accounts%0A%20%20%20%20%20locked-screen%0A%20%20%20%20%20restore-vault%0A%20%20%20%20%20identicon%0A%20%20%20%20%20config%0A%20%20%20%20%20info%0A%20%20%20%5D%0A%20%20%20%5Breducers%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20metamask%0A%20%20%20%20%20identities%0A%20%20%20%5D%0A%20%20%20%5Bactions%7C%0A%20%20%20%20%20%5BbackgroundConnection%5D%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%5D%3A-%3E%5Bactions%5D%0A%20%20%20%5Bactions%5D%3A-%3E%5Breducers%5D%0A%20%20%20%5Breducers%5D%3A-%3E%5Bcomponents%5D%0A%5D%0A%0A%5Bweb%20dapp%7C%0A%20%20%5Bui%20code%5D%0A%20%20%5Bweb3%5D%0A%20%20%5Bmetamask-inpage%5D%0A%20%20%0A%20%20%5B%3Cactor%3Eui%20developer%5D%0A%20%20%5Bui%20developer%5D-%3E%5Bui%20code%5D%0A%20%20%5Bui%20code%5D%3C-%3E%5Bweb3%5D%0A%20%20%5Bweb3%5D%3C-%3E%5Bmetamask-inpage%5D%0A%5D%0A%0A%5Bmetamask-background%7C%0A%20%20%5Bprovider-engine%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bid%20store%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%3E%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%3C-%3E%5Bid%20store%5D%0A%20%20%5Bconfig%20manager%7C%0A%20%20%20%20%5Brpc%20configuration%5D%0A%20%20%20%20%5Bencrypted%20keys%5D%0A%20%20%20%20%5Bwallet%20nicknames%5D%0A%20%20%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%5Bconfig%20manager%5D%0A%20%20%5Bid%20store%5D%3C-%3E%5Bconfig%20manager%5D%0A%5D%0A%0A%5Buser%5D%3C-%3E%5Bmetamask-ui%5D%0A%0A%5Buser%5D%3C%3A--%3A%3E%5Bweb%20dapp%5D%0A%0A%5Bmetamask-contentscript%7C%0A%20%20%5Bplugin%20restart%20detector%5D%0A%20%20%5Brpc%20passthrough%5D%0A%5D%0A%0A%5Brpc%20%7C%0A%20%20%5Bethereum%20blockchain%20%7C%0A%20%20%20%20%5Bcontracts%5D%0A%20%20%20%20%5Baccounts%5D%0A%20%20%5D%0A%5D%0A%0A%5Bweb%20dapp%5D%3C%3A--%3A%3E%5Bmetamask-contentscript%5D%0A%5Bmetamask-contentscript%5D%3C-%3E%5Bmetamask-background%5D%0A%5Bmetamask-background%5D%3C-%3E%5Bmetamask-ui%5D%0A%5Bmetamask-background%5D%3C-%3E%5Brpc%5D%0A

@ -157,6 +157,9 @@
"addMemo": {
"message": "Notiz hinzufügen"
},
"addMoreNetworks": {
"message": "weitere Netzwerke manuell hinzufügen"
},
"addNetwork": {
"message": "Netzwerk hinzufügen"
},
@ -282,9 +285,6 @@
"approvedAsset": {
"message": "Genehmigtes Asset"
},
"areYouDeveloper": {
"message": "Sind Sie ein Entwickler?"
},
"areYouSure": {
"message": "Sind Sie sicher?"
},
@ -438,7 +438,7 @@
"message": "$1 mit Wyre kaufen"
},
"buyWithWyreDescription": {
"message": "Wyre ermöglicht Ihnen die Verwendung einer Kreditkarte zum direkten Einzahlen von $1 auf Ihr MetaMask-Konto."
"message": "Einfaches Onboarding für Käufe bis zu 1000 $. Schnelle interaktive Überprüfung von Käufen mit hohem Limit. Unterstützt Debit-/Kreditkarte, Apple Pay, Banküberweisungen. Verfügbar in über 100 Ländern. Einzahlung von Token auf Ihr MetaMask-Konto"
},
"bytes": {
"message": "Bytes"
@ -466,6 +466,13 @@
"message": "Für eine Transaktion im Wert von $1 muss die Gasgebühr um mindestens 10 % erhöht werden, damit sie vom Netz erkannt wird.",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "Tausch für 1 $ abbrechen",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "Tausch kostenlos abbrechen"
},
"cancellationGasFee": {
"message": "Stornierungs-Gasgebühr"
},
@ -732,7 +739,7 @@
},
"customGasSettingToolTipMessage": {
"message": "$1 verwenden, um den Gaspreis anzupassen. Das kann verwirrend sein, wenn Sie damit nicht vertraut sind. Interaktion auf eigene Gefahr.",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "Höhere Gebühren können Bearbeitungszeiten verkürzen, wofür es allerdings keine Garantie gibt."
@ -818,7 +825,7 @@
},
"depositCrypto": {
"message": "$1 einzahlen",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "Beschreibung"
@ -1196,9 +1203,6 @@
"failureMessage": {
"message": "Etwas ist schief gelaufen und wir konnten die Aktion nicht abschließen"
},
"fakeTokenWarning": {
"message": "Jeder kann ein Token erstellen, einschließlich der Erstellung gefälschter Versionen bestehender Token. Erfahren Sie mehr über $1"
},
"fast": {
"message": "Schnell"
},
@ -1463,7 +1467,7 @@
},
"highGasSettingToolTipMessage": {
"message": "Hohe Wahrscheinlichkeit, auch in volatilen Märkten. Verwenden Sie $1, um Schwankungen im Netzwerkverkehr, die z. B. durch den Ausfall beliebter NFTs entstehen, abzudecken.",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "hoch"
@ -1766,7 +1770,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "Verwenden Sie $1, um auf einen günstigeren Preis zu warten. Zeitschätzungen sind viel ungenauer, da die Preise nicht vorhersehbar sind.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "niedrig"
@ -1810,7 +1814,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "Verwenden Sie $1 für schnelle Verarbeitung zum aktuellen Marktpreis.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": " Memo"
@ -1943,6 +1947,9 @@
"network": {
"message": "Netzwerk:"
},
"networkAddedSuccessfully": {
"message": "Netzwerk erfolgreich hinzugefügt!"
},
"networkDetails": {
"message": "Netzwerkdetails"
},
@ -2059,6 +2066,9 @@
"message": "Nonce ist höher als vorgeschlagen nonce von $1",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "NFT"
},
"nftTokenIdPlaceholder": {
"message": "Token-ID eingeben"
},
@ -2130,7 +2140,7 @@
},
"notifications10ActionText": {
"message": "Einstellungen ansehen",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "Die verbesserte Token-Erkennung ist derzeit in den Netzwerken Ethereum Mainnet, Polygon, BSC und Avalanche verfügbar. Es gibt bald mehr!"
@ -2225,7 +2235,7 @@
},
"notifications8ActionText": {
"message": "Zu den erweiterten Einstellungen gehen",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "Ab MetaMask v10.4.0 benötigen Sie kein Ledger Live mehr, um Ihr Ledger Gerät mit MetaMask zu verbinden.",
@ -2261,10 +2271,6 @@
"notificationsMarkAllAsRead": {
"message": "Alle als gelesen markieren"
},
"numberOfNewTokensDetected": {
"message": "$1 neue Tokens in diesem Konto gefunden\n",
"description": "$1 is the number of new tokens detected"
},
"ofTextNofM": {
"message": "von"
},
@ -2344,9 +2350,6 @@
"message": "Öffnen Sie MetaMask im Vollbildmodus, um Ihren Ledger über WebHID zu verbinden.",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "Prüfen Sie den Quellcode"
},
"optional": {
"message": "Optional"
},
@ -2473,7 +2476,7 @@
},
"preferredLedgerConnectionType": {
"message": "Bevorzugter Ledger-Verbindungstyp",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "Swap wird vorbereitet ..."
@ -2877,6 +2880,12 @@
"showAdvancedGasInlineDescription": {
"message": "Wählen Sie dies aus, um den Gaspreis und die Limitkontrollen direkt auf den Senden- und Bestätigen-Bildschirmen anzuzeigen."
},
"showCustomNetworkList": {
"message": "Benutzerdefinierte Netzwerkliste anzeigen"
},
"showCustomNetworkListDescription": {
"message": "Diese Option zeigt eine Liste von Netzwerken mit vorausgefüllten Details an, sobald ein neues Netzwerk hinzugefügt wird."
},
"showFiatConversionInTestnets": {
"message": "Umwandlung auf Testnets anzeigen"
},
@ -2967,10 +2976,6 @@
"snapInstallWarningCheck": {
"message": "Um zu bestätigen, dass Sie alles verstanden haben, kreuzen Sie alles an."
},
"snapInstallWarningKeyAccess": {
"message": "Sie gewähren dem Snap „$1“ wichtige Zugriffsrechte. Dies kann nicht rückgängig gemacht werden und gibt „$1“ Kontrolle über Ihre Konten und Vermögenswerte. Stellen Sie sicher, dass Sie „$1“ vertrauen, bevor Sie fortfahren.",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "Für diesen Snap werden die folgenden Berechtigungen beantragt:"
},
@ -2986,6 +2991,9 @@
"snapsToggle": {
"message": "Ein Snap wird nur ausgeführt, wenn er aktiviert ist"
},
"someNetworksMayPoseSecurity": {
"message": "Einige Netzwerke können Sicherheits- und/oder Datenschutzrisiken bergen. Informieren Sie sich über die Risiken, bevor Sie ein Netzwerk hinzufügen und nutzen."
},
"somethingWentWrong": {
"message": "Hoppla! Da hat etwas nicht geklappt."
},
@ -3075,7 +3083,7 @@
"message": "Status"
},
"statusConnected": {
"message": "Verbunden"
"message": "Verbinden"
},
"statusNotConnected": {
"message": "Nicht verbunden"
@ -3548,6 +3556,10 @@
"switchNetworks": {
"message": "Netzwerk wechseln"
},
"switchToNetwork": {
"message": "Zu 1 $ wechseln",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "Zu diesem Konto wechseln"
},
@ -3635,7 +3647,7 @@
},
"toggleTestNetworks": {
"message": "$1 Test-Netzwerke",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "Token"
@ -3658,18 +3670,6 @@
"tokenDetails": {
"message": "Token-Details"
},
"tokenDetection": {
"message": "Token-Erkennung"
},
"tokenDetectionAlertMessage": {
"message": "Die Token-Erkennung ist derzeit für $1 verfügbar. $2"
},
"tokenDetectionAnnouncement": {
"message": "Neu! Verbesserte Token Erkennung ist im Ethereum Mainnet als experimentelle Funktion verfügbar. $1"
},
"tokenDetectionToggleDescription": {
"message": "Die Token-API von ConsenSys sammelt eine Liste von Token aus verschiedenen Token-Listen von Drittanbietern. Wenn Sie diese Funktion deaktivieren, werden keine neuen Token mehr erkannt, die zu Ihrer Wallet hinzugefügt werden, aber die Option zur Suche nach Token für den Import bleibt erhalten."
},
"tokenId": {
"message": "Token-ID"
},
@ -3851,6 +3851,9 @@
"unknownCameraErrorTitle": {
"message": "Hoppla! Etwas ist schiefgegangen ..."
},
"unknownCollection": {
"message": "Unbenannte Sammlung"
},
"unknownNetwork": {
"message": "Unbekanntes privates Netzwerk"
},
@ -3901,12 +3904,6 @@
"usePhishingDetectionDescription": {
"message": "Zeigt eine Warnung für Phishing-Domänen, die Ethereum Benutzer ansprechen"
},
"useTokenDetection": {
"message": "Token-Erkennung verwenden"
},
"useTokenDetectionDescription": {
"message": "Wir verwenden Drittanbieter-API, um neue an Ihre Wallet gesendete Token zu erkennen und anzuzeigen. Deaktivieren Sie diese, wenn Sie nicht möchten, dass MetaMask Daten von diesen Diensten abruft."
},
"useTokenDetectionPrivacyDesc": {
"message": "Die automatische Anzeige der an Ihr Konto gesendeten Token erfordert die Kommunikation mit Servern von Drittanbietern, um die Bilder der Token abzurufen. Diese Server haben Zugriff auf Ihre IP-Adresse."
},
@ -3962,7 +3959,7 @@
"description": "$1 is the action type. e.g (Account, Transaction, Swap)"
},
"visitWebSite": {
"message": "Gehe zu unserer Webseite"
"message": "Besuchen Sie unsere Webseite"
},
"walletConnectionGuide": {
"message": "unsere Hardware-Wallet-Verbindungsanleitung"
@ -3987,6 +3984,9 @@
"walletCreationSuccessTitle": {
"message": "Wallet-Erstellung erfolgreich"
},
"wantToAddThisNetwork": {
"message": "Möchten Sie dieses Netzwerk hinzufügen?"
},
"warning": {
"message": "Warnung"
},
@ -4049,6 +4049,10 @@
"yesLetsTry": {
"message": "Ja, versuchen wir es"
},
"youHaveAddedAll": {
"message": "Sie haben alle beliebten Netzwerke hinzugefügt. Sie können weitere Netzwerke entdecken 1 $ oder Sie können 2 $",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "Sie müssen Zugriff auf die Kamera erlauben, um diese Funktion nutzen zu können."
},

@ -157,6 +157,9 @@
"addMemo": {
"message": "Προσθήκη σημειώματος"
},
"addMoreNetworks": {
"message": "προσθέστε περισσότερα δίκτυα χειροκίνητα"
},
"addNetwork": {
"message": "Προσθήκη Δικτύου"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "Ειδοποιήσεις"
},
"allOfYour": {
"message": "Όλα σας τα $1",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "Επιτρέψτε σε αυτή την εξωτερική επέκταση να:"
},
@ -263,6 +270,10 @@
"approve": {
"message": "Έγκριση"
},
"approveAllTokensTitle": {
"message": "Δίνετε άδεια για να αποκτήσετε πρόσβαση σε όλα σας τα $1;",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "Έγκριση και Εγκατάσταση"
},
@ -282,9 +293,6 @@
"approvedAsset": {
"message": "Εγκεκριμένο περιουσιακό στοιχείο"
},
"areYouDeveloper": {
"message": "Είστε προγραμματιστής;"
},
"areYouSure": {
"message": "Είστε βέβαιος/η;"
},
@ -438,7 +446,7 @@
"message": "Αγοράστε $1 με το Wyre"
},
"buyWithWyreDescription": {
"message": "Το Wyre σας επιτρέπει να χρησιμοποιήσετε μια πιστωτική κάρτα για να καταθέσετε $1 απευθείας στον λογαριασμό σας MetaTask."
"message": "Εύκολη ενσωμάτωση για αγορές μέχρι και $ 1000. Γρήγορη διαδραστική επαλήθευση αγοράς υψηλού ορίου. Υποστηρίζει χρεωστικές/πιστωτικές κάρτες, Apple Pay, Τραπεζικές Μεταφορές. Διαθέσιμο σε 100+ χώρες. Καταθέσεις token στον λογαριασμό σας MetaMask"
},
"bytes": {
"message": "Bytes"
@ -466,6 +474,13 @@
"message": "Για να $1 τη συναλλαγή, τα τέλη συναλλαγής πρέπει να αυξηθούν κατά τουλάχιστον 10% ώστε να αναγνωριστούν από το δίκτυο.",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "Ακυρώστε τη συναλλαγή για ~$1",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "Ακυρώστε τη συναλλαγή δωρεάν"
},
"cancellationGasFee": {
"message": "Ακύρωση Χρέωσης Αερίου"
},
@ -732,7 +747,7 @@
},
"customGasSettingToolTipMessage": {
"message": "Χρησιμοποιήστε 1 $ για να προσαρμόσετε την τιμή του τέλους συναλλαγής. Αυτό μπορεί να προκαλέσει σύγχυση, αν δεν είστε εξοικειωμένοι. Επεξεργαστείτε το με δικό σας ρίσκο.",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "Η αύξηση των τελών μπορεί να μειώσει τους χρόνους επεξεργασίας, αλλά αυτό δεν είναι εγγυημένο."
@ -818,7 +833,7 @@
},
"depositCrypto": {
"message": "Κατάθεση $1",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "Περιγραφή"
@ -1196,9 +1211,6 @@
"failureMessage": {
"message": "Κάτι πήγε λάθος και δεν μπορέσαμε να ολοκληρώσουμε την ενέργεια"
},
"fakeTokenWarning": {
"message": "Οποιοσδήποτε μπορεί να δημιουργήσει ένα token, συμπεριλαμβανομένης της δημιουργίας ψεύτικων εκδόσεων των υφιστάμενων tokens. Μάθετε περισσότερα γι'αυτό $1"
},
"fast": {
"message": "Γρήγορα"
},
@ -1277,6 +1289,9 @@
"functionApprove": {
"message": "Λειτουργία: Έγκριση"
},
"functionSetApprovalForAll": {
"message": "Λειτουργία: SetApprovalForAll"
},
"functionType": {
"message": "Τύπος Λειτουργίας"
},
@ -1463,7 +1478,7 @@
},
"highGasSettingToolTipMessage": {
"message": "Χρησιμοποιήστε $1 για να καλύψετε απότομες αυξήσεις της κίνησης του δικτύου λόγω των δημοφιλών ξεκινημάτων NFT.",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "υψηλό"
@ -1617,6 +1632,9 @@
"invalidSeedPhrase": {
"message": "Μη έγκυρη Μυστική Φράση Ανάκτησής"
},
"invalidSeedPhraseCaseSensitive": {
"message": "Μη έγκυρη εισαγωγή! Η Μυστική σας Φράση Ανάκτησης κάνει διάκριση πεζών-κεφαλαίων."
},
"ipfsGateway": {
"message": "Πύλη IPFS"
},
@ -1766,7 +1784,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "Χρησιμοποιήστε 1 $ για να περιμένετε για μια φθηνότερη τιμή. Οι εκτιμήσεις του χρόνου είναι πολύ λιγότερο ακριβείς καθώς οι τιμές είναι κάπως απρόβλεπτες.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "χαμηλό"
@ -1810,7 +1828,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "Χρησιμοποιήστε $1 για γρήγορη επεξεργασία στην τρέχουσα τιμή της αγοράς.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "σημείωμα"
@ -1892,6 +1910,19 @@
"message": "επαληθεύστε τα στοιχεία δικτύου",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"mismatchedChainRecommendation": {
"message": "Σας προτείνουμε να $1 πριν συνεχίσετε.",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "Σύμφωνα με τις καταχωρήσεις μας, το όνομα δικτύου ίσως δεν ταιριάζει με το αναγνωριστικό αλυσίδας."
},
"mismatchedNetworkSymbol": {
"message": "Το σύμβολο νομίσματος που υποβλήθηκε δεν ταιριάζει με αυτό που αναμενόταν για αυτό το αναγνωριστικό αλυσίδας."
},
"mismatchedRpcUrl": {
"message": "Σύμφωνα με τις καταχωρήσεις μας, η τιμή RPC URL που υποβλήθηκε δεν ταιριάζει με κάποιον γνωστό πάροχο για αυτό το αναγνωριστικό αλυσίδας."
},
"missingNFT": {
"message": "Δεν βλέπετε το NFT σας;"
},
@ -1943,6 +1974,9 @@
"network": {
"message": "Δίκτυο:"
},
"networkAddedSuccessfully": {
"message": "Το δίκτυο προστέθηκε επιτυχώς!"
},
"networkDetails": {
"message": "Λεπτομέρειες Δικτύου"
},
@ -2059,6 +2093,9 @@
"message": "Το Nonce είναι υψηλότερο από το προτεινόμενο nonce του $1",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "NFT"
},
"nftTokenIdPlaceholder": {
"message": "Εισάγετε το συλλεκτικό αναγνωριστικό"
},
@ -2130,7 +2167,7 @@
},
"notifications10ActionText": {
"message": "Μετάβαση στις Ρυθμίσεις",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "Ο βελτιωμένος εντοπισμός token είναι προς το παρόν διαθέσιμος στα δίκτυα Ethereum Mainnet, Polygon, BSC και Avalanche. Περισσότερα προσεχώς!"
@ -2154,11 +2191,21 @@
"message": "Ενεργοποίηση dark mode"
},
"notifications12Description": {
"message": "Το Dark Mode (σκουρόχρωμη λειτουργία) θα ενεργοποιηθεί για νέους χρήστες ανάλογα με τις προτιμήσεις του συστήματός τους. Εάν είστε ήδη χρήστης, ενεργοποιήστε τo dark mode με μη αυτόματο τρόπο στην ενότητα Ρυθμίσεις -> Πειραματικές."
"message": "Έφτασε επιτέλους το Dark Mode (σκουρόχρωμη λειτουργία)! Για να το ενεργοποιήσετε, μεταβείτε στις Ρυθμίσεις -> Πειραματικό και επιλέξτε μία από τις επιλογές εμφάνισης: Light, Dark, Σύστημα."
},
"notifications12Title": {
"message": "Θέλετε dark mode; Τώρα έχετε dark mode! 🕶🦊"
},
"notifications13ActionText": {
"message": "Προβολή λίστας προσαρμοσμένων δικτύων"
},
"notifications13Description": {
"message": "Τώρα μπορείτε να προσθέσετε εύκολα τα ακόλουθα δημοφιλή προσαρμοσμένα δίκτυα: Arbitrum, Avalanche, Binance Smart Chain, Fantom, Harmony, Optimism, Palm and Polygon! Για να ενεργοποιήσετε αυτήν τη λειτουργία, πηγάινετε στις Ρυθμίσεις -> Πειραματικό και ενεργοποιήστε το «Προβολή λίστας προσαρμοσμένων δικτύων»!",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "Προσθήκη Δημοφιλών Δικτύων"
},
"notifications1Description": {
"message": "Οι χρήστες του MetaMask Mobile μπορούν τώρα να ανταλλάξουν tokens μέσα στο κινητό τους πορτοφόλι. Σαρώστε τον κωδικό QR για να πάρετε την εφαρμογή για κινητά και να αρχίσετε να ανταλλάζετε.",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2225,7 +2272,7 @@
},
"notifications8ActionText": {
"message": "Μεταβείτε στις Ρυθμίσεις για Προχωρημένους",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "Από τη MetaMask v10.4.0 και μετά, δεν χρειάζεστε πλέον το Ledger Live για να συνδέσετε τη συσκευή Ledger με το MetaMask.",
@ -2261,10 +2308,6 @@
"notificationsMarkAllAsRead": {
"message": "Επισήμανση όλων ως αναγνωσμένων"
},
"numberOfNewTokensDetected": {
"message": "$1 νέα token βρέθηκαν σε αυτόν τον λογαριασμό",
"description": "$1 is the number of new tokens detected"
},
"ofTextNofM": {
"message": "από"
},
@ -2344,9 +2387,6 @@
"message": "Ανοίξτε το MetaMask σε πλήρη οθόνη για να συνδέσετε το ledger σας μέσω WebHID.",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "Ελέγξτε τον πηγαίο κώδικα"
},
"optional": {
"message": "Προαιρετικό"
},
@ -2473,7 +2513,7 @@
},
"preferredLedgerConnectionType": {
"message": "Προτιμώμενος Τύπος Σύνδεσης Ledger",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "Προετοιμασία ανταλλαγής..."
@ -2676,6 +2716,14 @@
"revealTheSeedPhrase": {
"message": "Αποκάλυψη φράσης ανάκτησης"
},
"revokeAllTokensTitle": {
"message": "Ανάκληση άδειας πρόσβασης σε όλα σας τα $1;",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "Με την ανάκληση της άδειας, το ακόλουθο $1 δεν θα έχει πλέον πρόσβαση στο $2 σας",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Δοκιμαστικό Δίκτυο Rinkeby"
},
@ -2852,12 +2900,23 @@
"message": "Αποστολή $1",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "Προειδοποίηση: πρόκειται να στείλετε ένα συμβόλαιο token το οποίο ίσως καταλήξει σε απώλεια χρημάτων. $1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "Ορίστε ρυθμίσεις απορρήτου για προχωρημένους"
},
"setAdvancedPrivacySettingsDetails": {
"message": "Το MetaMask χρησιμοποιεί αυτές τις αξιόπιστες υπηρεσίες τρίτων για να ενισχύσει τη χρηστικότητα και την ασφάλεια των προϊόντων."
},
"setApprovalForAll": {
"message": "Ρύθμιση Έγκρισης Όλων"
},
"setApprovalForAllTitle": {
"message": "Έγκριση $1 χωρίς όριο δαπανών",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "Ρυθμίσεις"
},
@ -2877,6 +2936,12 @@
"showAdvancedGasInlineDescription": {
"message": "Επιλέξτε αυτό για να εμφανίσετε τις τιμές αερίου και να περιορίσετε τα στοιχεία ελέγχου απευθείας στις οθόνες αποστολής και επιβεβαίωσης."
},
"showCustomNetworkList": {
"message": "Προβολή Λίστας Προσαρμοσμένων Δικτύων"
},
"showCustomNetworkListDescription": {
"message": "Επιλέξτε αυτό για να εμφανιστεί μια λίστα δικτύων με προσυμπληρωμένα στοιχεία κατά την προσθήκη ενός νέου δικτύου."
},
"showFiatConversionInTestnets": {
"message": "Εμφάνιση Μετατροπής σε Δοκιμαστικά Δίκτυα"
},
@ -2920,7 +2985,7 @@
"message": "Υπογραφή"
},
"signNotice": {
"message": "Η υπογραφή αυτού του μηνύματος μπορεί να έχει\nεπικίνδυνες παρενέργειες. Υπογράφετε μηνύματα μόνο από\nτοποθεσίες που εμπιστεύεστε πλήρως με ολόκληρο τον λογαριασμό σας.\n  Αυτή η επικίνδυνη μέθοδος θα καταργηθεί σε μια μελλοντική έκδοση."
"message": "Η υπογραφή αυτού του μηνύματος μπορεί να έχει\nεπικίνδυνες παρενέργειες. Υπογράφετε μηνύματα μόνο από\nτοποθεσίες που εμπιστεύεστε πλήρως με ολόκληρο τον λογαριασμό σας.\n Αυτή η επικίνδυνη μέθοδος θα καταργηθεί σε μια μελλοντική έκδοση."
},
"signatureRequest": {
"message": "Αίτημα Υπογραφής"
@ -2967,10 +3032,6 @@
"snapInstallWarningCheck": {
"message": "Για να επιβεβαιώσετε ότι καταλαβαίνετε, επιλέξτε όλα τα πλαίσια ελέγχου."
},
"snapInstallWarningKeyAccess": {
"message": "Παρέχετε βασική πρόσβαση στο snap \"$1\". Αυτή η ενέργεια είναι μη αναστρέψιμη και παραχωρεί στο \"$1\" τον έλεγχο των λογαριασμών και των περιουσιακών σας στοιχείων. Βεβαιωθείτε ότι εμπιστεύεστε το \"$1\" πριν συνεχίσετε.",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "Αυτό το snap αιτείται τις παρακάτω άδειες:"
},
@ -2986,6 +3047,9 @@
"snapsToggle": {
"message": "Ένα snap θα εκτελεστεί μόνο εάν είναι ενεργοποιημένο"
},
"someNetworksMayPoseSecurity": {
"message": "Ορισμένα δίκτυα ενδέχεται να ενέχουν κινδύνους για την ασφάλεια ή/και το απόρρητο. Ενημερωθείτε για τους κινδύνους πριν προσθέσετε και χρησιμοποιήσετε ένα δίκτυο."
},
"somethingWentWrong": {
"message": "Ουπς! Κάτι πήγε στραβά."
},
@ -3548,6 +3612,10 @@
"switchNetworks": {
"message": "Αλλαγή Δικτύων"
},
"switchToNetwork": {
"message": "Εναλλαγή σε $1",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "Εναλλαγή σε αυτόν τον λογαριασμό"
},
@ -3635,7 +3703,7 @@
},
"toggleTestNetworks": {
"message": "$1 δοκιμαστικά δίκτυα",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "Διακριτικό"
@ -3658,18 +3726,6 @@
"tokenDetails": {
"message": "Λεπτομέρειες token"
},
"tokenDetection": {
"message": "Εντοπισμός token"
},
"tokenDetectionAlertMessage": {
"message": "Ο εντοπισμός token είναι επί του παρόντος διαθέσιμος στο $1. $2"
},
"tokenDetectionAnnouncement": {
"message": "Νέο! Η βελτιωμένη ανίχνευση token είναι διαθέσιμη στο Ethereum Mainnet ως πειραματικό χαρακτηριστικό. $1"
},
"tokenDetectionToggleDescription": {
"message": "Το token API της ConsenSys δημιουργεί μια λίστα με token από διάφορες λίστες token τρίτων. Εάν το απενεργοποιήσετε, θα σταματήσει ο εντοπισμός νέων token που προστίθενται στο πορτοφόλι σας, αλλά θα διατηρηθεί η επιλογή αναζήτησης token για εισαγωγή."
},
"tokenId": {
"message": "Αναγνωριστικό token"
},
@ -3851,6 +3907,9 @@
"unknownCameraErrorTitle": {
"message": "Ουπς! Κάτι πήγε στραβά...."
},
"unknownCollection": {
"message": "Ανώνυμη συλλογή"
},
"unknownNetwork": {
"message": "Άγνωστο Ιδιωτικό Δίκτυο"
},
@ -3901,12 +3960,6 @@
"usePhishingDetectionDescription": {
"message": "Εμφάνιση μιας προειδοποίησης για τομείς Απάτης Ηλεκτρονικού Ψαρέματος που στοχεύουν χρήστες του Ethereum"
},
"useTokenDetection": {
"message": "Χρήση Ανίχνευσης Token"
},
"useTokenDetectionDescription": {
"message": "Χρησιμοποιούμε API τρίτων για να εντοπίσουμε και να εμφανίσουμε νέα tokens που αποστέλλονται στο πορτοφόλι σας. Απενεργοποιήστε αν δεν θέλετε το MetaMask να τραβήξει δεδομένα από αυτές τις υπηρεσίες."
},
"useTokenDetectionPrivacyDesc": {
"message": "Η αυτόματη εμφάνιση των token που αποστέλλονται στον λογαριασμό σας συνεπάγεται επικοινωνία με διακομιστές τρίτων για τη λήψη εικόνων των token. Αυτοί οι διακομιστές θα έχουν πρόσβαση στη διεύθυνση IP σας."
},
@ -3987,6 +4040,9 @@
"walletCreationSuccessTitle": {
"message": "Επιτυχής δημιουργία πορτοφολιού"
},
"wantToAddThisNetwork": {
"message": "Θέλετε να προσθέσετε αυτό το δίκτυο;"
},
"warning": {
"message": "Προειδοποίηση"
},
@ -4049,6 +4105,10 @@
"yesLetsTry": {
"message": "Ναι, ας δοκιμάσουμε"
},
"youHaveAddedAll": {
"message": "Προσθέσατε όλα τα δημοφιλή δίκτυα. Μπορείτε να ανακαλύψετε περισσότερα δίκτυα $1 Ή μπορείτε να \n $2",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "Πρέπει να επιτρέψετε πρόσβαση στην κάμερα για να χρησιμοποιήσετε αυτήν τη λειτουργία."
},

File diff suppressed because it is too large Load Diff

@ -157,6 +157,9 @@
"addMemo": {
"message": "Añadir memo"
},
"addMoreNetworks": {
"message": "agregar más redes manualmente"
},
"addNetwork": {
"message": "Agregar red"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "Alertas"
},
"allOfYour": {
"message": "Todo su $1",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "Permitir que esta extensión externa haga lo siguiente:"
},
@ -263,6 +270,10 @@
"approve": {
"message": "Aprobar límite de gastos"
},
"approveAllTokensTitle": {
"message": "¿Dar permiso para acceder a todo su $1?",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "Aprobar e instalar"
},
@ -282,9 +293,6 @@
"approvedAsset": {
"message": "Activo aprobado"
},
"areYouDeveloper": {
"message": "¿Usted es desarrollador?"
},
"areYouSure": {
"message": "¿Está seguro?"
},
@ -438,7 +446,7 @@
"message": "Comprar $1 con Wyre"
},
"buyWithWyreDescription": {
"message": "Acceso fácil a transacciones de $ 1,000 o menos con verificación rápida y efectiva. Aceptamos tarjetas débito, crédito, Apple Pay y transferencias bancarias en más de 100 países. Tokens serán depósitados en su MetaMask."
"message": "Acceso fácil a compras de hasta $1,000. Verificación interactiva rápida de compra de límite alto. Acepta tarjeta de débito/crédito, Apple Pay y transferencias bancarias. Disponible para más de 100 países. Los tokens se depositarán en su cuenta MetaMask"
},
"bytes": {
"message": "Bytes"
@ -466,6 +474,13 @@
"message": "Para $1 una transacción, la tarifa de gas debe aumentar al menos un 10% para que sea reconocida por la red.",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "Cancelar el swap por ~$1",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "Cancelar el swap gratuitamente"
},
"cancellationGasFee": {
"message": "Cuota de gas por cancelación"
},
@ -650,7 +665,7 @@
"message": "Interacción con el contrato"
},
"convertTokenToNFTDescription": {
"message": "Hemos detectado que este activo es un NFT. MetaMask ahora tiene soporte nativo completo para NFTs. ¿Quieres eliminarlo de tu lista de tokens y añadirlo como un NFT?"
"message": "Hemos detectado que este activo es un NFT. MetaMask ahora tiene soporte nativo completo para NFT. ¿Quiere eliminarlo de su lista de tokens y añadirlo como un NFT?"
},
"convertTokenToNFTExistDescription": {
"message": "Hemos detectado que este recurso se ha agregado como NFT. ¿Quiere eliminarlo de su lista de tokens?"
@ -732,7 +747,7 @@
},
"customGasSettingToolTipMessage": {
"message": "Use $1 para personalizar el precio de gas. Esto puede ser confuso si no está familiarizado. Interactúe bajo su propio riesgo.",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "Aumentar la cuota puede disminuir los tiempos de procesamiento, pero no está garantizado."
@ -818,7 +833,7 @@
},
"depositCrypto": {
"message": "Deposite $1",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "Descripción"
@ -1196,9 +1211,6 @@
"failureMessage": {
"message": "Se produjo un error y no pudimos completar la acción"
},
"fakeTokenWarning": {
"message": "Cualquiera puede crear un token, incluso crear versiones falsas de tokens existentes. Aprenda más sobre $1"
},
"fast": {
"message": "Rápido"
},
@ -1277,6 +1289,9 @@
"functionApprove": {
"message": "Función: Aprobar"
},
"functionSetApprovalForAll": {
"message": "Función: SetApprovalForAll"
},
"functionType": {
"message": "Tipo de función"
},
@ -1463,7 +1478,7 @@
},
"highGasSettingToolTipMessage": {
"message": "Alta probabilidad, incluso en mercados volátiles. Use $1 para cubrir aumentos repentinos en el tráfico de la red debido a cosas como caídas de NFT populares.",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "alto"
@ -1521,7 +1536,7 @@
"message": "agregar activo"
},
"importTokensCamelCase": {
"message": "AGREGAR TOKENS"
"message": "Agregar Tokens"
},
"importWallet": {
"message": "Importar cartera"
@ -1617,6 +1632,9 @@
"invalidSeedPhrase": {
"message": "Frase secreta de recuperación no válida"
},
"invalidSeedPhraseCaseSensitive": {
"message": "¡Entrada inválida! La frase secreta de recuperación distingue entre mayúsculas y minúsculas."
},
"ipfsGateway": {
"message": "Puerta de enlace de IPFS"
},
@ -1766,7 +1784,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "Utilice $1 para esperar un precio más bajo. Las estimaciones de tiempo son mucho menos precisas ya que los precios son algo imprevisibles.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "bajo"
@ -1810,7 +1828,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "Utilice $1 para un procesamiento rápido al precio actual del mercado.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "memorándum"
@ -1892,6 +1910,19 @@
"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."
},
"mismatchedChainRecommendation": {
"message": "Recomendamos que usted $1 antes de proceder.",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "Según nuestros registros, es posible que el nombre de la red no coincida correctamente con este ID de cadena."
},
"mismatchedNetworkSymbol": {
"message": "El símbolo de moneda enviado no coincide con lo que esperamos para este ID de cadena."
},
"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?"
},
@ -1943,6 +1974,9 @@
"network": {
"message": "Red:"
},
"networkAddedSuccessfully": {
"message": "¡Red añadida exitosamente!"
},
"networkDetails": {
"message": "Detalles de la red"
},
@ -2059,6 +2093,9 @@
"message": "El nonce es superior al nonce sugerido de $1",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "NFT"
},
"nftTokenIdPlaceholder": {
"message": "Ingresa el ID del token"
},
@ -2130,7 +2167,7 @@
},
"notifications10ActionText": {
"message": "Vaya a configuración",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "En este momento, la detección mejorada de token está disponible en las redes Ethereum Mainnet, Polygon, BSC y Avalanche. ¡Y habrá más!"
@ -2154,11 +2191,21 @@
"message": "Habilitar modo oscuro"
},
"notifications12Description": {
"message": "¡El modo oscuro en Extension finalmente está aquí! Para encenderlo, vaya a Configuración -> Experimental y seleccione una de las opciones de visualización: Claro, Oscuro, Sistema."
"message": "¡El modo oscuro en Extensión finalmente está aquí! Para activarlo, vaya a Configuración -> Experimental y seleccione una de las opciones de visualización: Claro, Oscuro, Sistema."
},
"notifications12Title": {
"message": "¿Cuándo estará disponible el modo oscuro? ¡Ahora! 🕶🦊"
},
"notifications13ActionText": {
"message": "Mostrar lista de redes personalizadas"
},
"notifications13Description": {
"message": "Ahora puede agregar fácilmente las siguientes redes personalizadas populares: Arbitrum, Avalanche, Binance Smart Chain, Fantom, Harmony, Optimism, Palm y Polygon. Para habilitar esta función, vaya a Configuración -> Experimental y ¡active \"Mostrar lista de redes personalizadas\"!",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "Añadir redes populares"
},
"notifications1Description": {
"message": "Los usuarios de la aplicación móvil de MetaMask ahora pueden canjear tokens en su cartera móvil. Escanee el código QR para obtener la aplicación móvil y comience a canjear.",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2225,7 +2272,7 @@
},
"notifications8ActionText": {
"message": "Ir a Configuración Avanzada",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "A partir de MetaMask v10.4.0, ya no necesita Ledger Live para conectar su dispositivo Ledger a MetaMask.",
@ -2261,10 +2308,6 @@
"notificationsMarkAllAsRead": {
"message": "Marcar todo como leído"
},
"numberOfNewTokensDetected": {
"message": "Se encontraron tokens nuevos de $1 en esta cuenta",
"description": "$1 is the number of new tokens detected"
},
"ofTextNofM": {
"message": "de"
},
@ -2344,9 +2387,6 @@
"message": "Abra MetaMask en pantalla completa para conectar su Ledger a través de WebHID.",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "Compruebe el código fuente"
},
"optional": {
"message": "Opcional"
},
@ -2473,7 +2513,7 @@
},
"preferredLedgerConnectionType": {
"message": "Tipo de conexión de Ledger preferida",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "Preparando swap..."
@ -2676,6 +2716,14 @@
"revealTheSeedPhrase": {
"message": "Revelar frase semilla"
},
"revokeAllTokensTitle": {
"message": "¿Revocar permiso para acceder a todo su $1?",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "Al revocar permisos, el/la siguiente $1 no tendrá más acceso a su $2",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Red de prueba Rinkeby"
},
@ -2852,12 +2900,23 @@
"message": "Enviando $1",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "Advertencia: está a punto de enviar un contrato de token que podría dar lugar a una pérdida de fondos. $1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "Configuración avanzada de privacidad"
},
"setAdvancedPrivacySettingsDetails": {
"message": "MetaMask utiliza estos servicios de terceros de confianza para mejorar la usabilidad y la seguridad de los productos."
},
"setApprovalForAll": {
"message": "Establecer aprobación para todos"
},
"setApprovalForAllTitle": {
"message": "Aprobar $1 sin límite preestablecido",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "Configuración"
},
@ -2877,6 +2936,12 @@
"showAdvancedGasInlineDescription": {
"message": "Seleccione esta opción para mostrar el precio del gas y limitar los controles directamente en las pantallas de envío y confirmación."
},
"showCustomNetworkList": {
"message": "Mostrar lista de redes personalizadas"
},
"showCustomNetworkListDescription": {
"message": "Seleccione esto para mostrar una lista de redes con detalles precargados al agregar una red nueva."
},
"showFiatConversionInTestnets": {
"message": "Mostrar conversión en redes de prueba"
},
@ -2967,10 +3032,6 @@
"snapInstallWarningCheck": {
"message": "Para confirmar que comprende, marque todo."
},
"snapInstallWarningKeyAccess": {
"message": "Está otorgando acceso clave al complemento \"$1\". Esto es irrevocable y le otorga a \"$1\" el control de sus cuentas y activos. Asegúrese de confiar en \"$1\" antes de continuar.",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "Este complemento solicita los siguientes permisos:"
},
@ -2986,6 +3047,9 @@
"snapsToggle": {
"message": "Un complemento solo se ejecutará si está habilitado"
},
"someNetworksMayPoseSecurity": {
"message": "Algunas redes pueden presentar riesgos de seguridad y/o privacidad. Comprenda los riesgos antes de agregar y utilizar una red."
},
"somethingWentWrong": {
"message": "Lo lamentamos, se produjo un error."
},
@ -3548,6 +3612,10 @@
"switchNetworks": {
"message": "Cambiar redes"
},
"switchToNetwork": {
"message": "Cambiar a $1",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "Cambiar a esta cuenta"
},
@ -3635,7 +3703,7 @@
},
"toggleTestNetworks": {
"message": "$1 redes de prueba",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "Token"
@ -3658,18 +3726,6 @@
"tokenDetails": {
"message": "Detalles del token"
},
"tokenDetection": {
"message": "Detección del token"
},
"tokenDetectionAlertMessage": {
"message": "La detección de tókens está actualmente disponible en $1. $2"
},
"tokenDetectionAnnouncement": {
"message": "¡Nuevo! La detección de tokens mejorada está disponible en la Mainnet de Ethereum como funcionalidad experimental. $1"
},
"tokenDetectionToggleDescription": {
"message": "La API de tokens de ConsenSys agrega una lista de tokens de varias listas de tokens de terceros. Al desactivarla se dejará de detectar nuevos tokens agregados a su billetera, pero se mantendrá la opción de buscar tokens para importar."
},
"tokenId": {
"message": "Identificador de Token"
},
@ -3851,6 +3907,9 @@
"unknownCameraErrorTitle": {
"message": "Lo lamentamos, se produjo un error…"
},
"unknownCollection": {
"message": "Colección sin nombre"
},
"unknownNetwork": {
"message": "Red privada desconocida"
},
@ -3901,12 +3960,6 @@
"usePhishingDetectionDescription": {
"message": "Mostrar una advertencia respecto de los dominios de phishing dirigidos a los usuarios de Ethereum"
},
"useTokenDetection": {
"message": "Usar detección de token"
},
"useTokenDetectionDescription": {
"message": "Utilizamos API de terceros para detectar y mostrar nuevos tokens enviados a su cartera. Desactive si no desea que MetaMask extraiga datos de esos servicios."
},
"useTokenDetectionPrivacyDesc": {
"message": "La visualización automática de tokens enviados a su cuenta implica la comunicación con servidores de terceros para obtener imágenes de tokens. Esos servicios tendrán acceso a su dirección IP."
},
@ -3987,6 +4040,9 @@
"walletCreationSuccessTitle": {
"message": "Creación exitosa de la cartera"
},
"wantToAddThisNetwork": {
"message": "¿Desea añadir esta red?"
},
"warning": {
"message": "Advertencia"
},
@ -4049,6 +4105,10 @@
"yesLetsTry": {
"message": "Sí, intentémoslo"
},
"youHaveAddedAll": {
"message": "Ha agregado todas las redes populares. Puede descubrir más redes $1 o puede $2",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "Necesita permitir el acceso a la cámara para usar esta función."
},

@ -1047,9 +1047,6 @@
"failureMessage": {
"message": "Se produjo un error y no pudimos finalizar la acción"
},
"fakeTokenWarning": {
"message": "Cualquiera puede crear un token, incluso crear versiones falsas de tokens existentes. Aprenda más sobre $1"
},
"fast": {
"message": "Rápido"
},
@ -1533,7 +1530,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "Utilice $1 para esperar un precio más bajo. Las estimaciones de tiempo son mucho menos precisas ya que los precios son algo imprevisibles.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "baja"
@ -1565,7 +1562,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "Utilice $1 para un procesamiento rápido al precio actual del mercado.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "nota"
@ -1911,7 +1908,7 @@
},
"notifications8ActionText": {
"message": "Ir a Configuración Avanzada",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "A partir de MetaMask v10.4.0, ya no necesita Ledger Live para conectar su dispositivo Ledger a MetaMask.",
@ -2085,7 +2082,7 @@
},
"preferredLedgerConnectionType": {
"message": "Tipo de conexión de Ledger preferida",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"prev": {
"message": "Ant."
@ -3007,7 +3004,7 @@
},
"toggleTestNetworks": {
"message": "$1 redes de prueba",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "Token"
@ -3021,9 +3018,6 @@
"tokenDecimalFetchFailed": {
"message": "Se requieren los decimales del token."
},
"tokenDetectionAnnouncement": {
"message": "¡Nuevo! La detección de tokens mejorada está disponible en la Mainnet de Ethereum como funcionalidad experimental. $1"
},
"tokenId": {
"message": "ID del token"
},
@ -3236,12 +3230,6 @@
"usePhishingDetectionDescription": {
"message": "Mostrar una advertencia respecto de los dominios de phishing dirigidos a los usuarios de Ethereum"
},
"useTokenDetection": {
"message": "Usar detección de token"
},
"useTokenDetectionDescription": {
"message": "Utilizamos API de terceros para detectar y mostrar nuevos tokens enviados a su cartera. Desactive si no desea que MetaMask extraiga datos de esos servicios."
},
"usedByClients": {
"message": "Usado por una variedad de clientes distintos"
},

File diff suppressed because it is too large Load Diff

@ -157,6 +157,9 @@
"addMemo": {
"message": "म"
},
"addMoreNetworks": {
"message": "मअल रप स अधिक नटवरक ज"
},
"addNetwork": {
"message": "नटवरक ज"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "चवनि"
},
"allOfYour": {
"message": "आपक सभ $1",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "इस बहर एकसटशन क इसक अनमति:"
},
@ -263,6 +270,10 @@
"approve": {
"message": "खरच स अनित कर"
},
"approveAllTokensTitle": {
"message": "आपक सभ $1 क एकस करनिए अनमति",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "सि और इल कर"
},
@ -282,9 +293,6 @@
"approvedAsset": {
"message": "सत एसट"
},
"areYouDeveloper": {
"message": "क आप एक डवलपर ह?"
},
"areYouSure": {
"message": "क आप सिित ह?"
},
@ -435,10 +443,10 @@
"description": "$1 represents the crypto symbol to be purchased"
},
"buyWithWyre": {
"message": "Wyre क जरि $1 खर"
"message": "Wyre क $1 खर"
},
"buyWithWyreDescription": {
"message": "Wyre आपक अपन MetaMask ख $1 जम करनिए डिट कड क उपयग करनि"
"message": "$1000 तक क खरिए आसन ऑनबिग। तटरिव उचच स खरद सतपन। डिट / किट कड, ऐपपल प, बक टसफर क समरथन करत। 100+ द उपलबध ह। टकन आपकक ख जम"
},
"bytes": {
"message": "बइट"
@ -466,6 +474,13 @@
"message": "किनदन क $1 करनिए गस शक म कम स कम 10% कििए ति उसटवरक दयतिल सक।",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "~$1 मप रदद कर",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "मत मप रदद कर"
},
"cancellationGasFee": {
"message": "रदकरण गस शक"
},
@ -650,7 +665,7 @@
"message": "अनध इटरशन"
},
"convertTokenToNFTDescription": {
"message": "हमनि एसट एक एनएफट। MetaMask कस अब एनएफटिए पण सय सप। क आप इस अपनकन स हटहत और इस एनएफटप महत?"
"message": "हमनिह सपति एक एनएफट। मक कस अब एनएफटिए पण द समरथन। क आप इस अपनकन स हटहत और इस एनएफटप महत?"
},
"convertTokenToNFTExistDescription": {
"message": "हमनि इस एसट क एक एनएफटप म गय। क आप इस अपनकन स हटहत?"
@ -732,7 +747,7 @@
},
"customGasSettingToolTipMessage": {
"message": "गस कमत क अनित करनिए $1 क उपयग कर। यदि आप परिित नहमक ह सकत। अपनि पर बतचत कर।",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "शक बढरसकरण समय म कम सकत, लिन इसक नह।"
@ -818,7 +833,7 @@
},
"depositCrypto": {
"message": "$1 जम कर",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "विवरण"
@ -1196,9 +1211,6 @@
"failureMessage": {
"message": "कछ गलत हआ और हम करवई क करन असमरथ रह"
},
"fakeTokenWarning": {
"message": "कई भकन बन सकत, जिसमकन क नकलकरण क बनिल ह। $1 क और अधिक ज"
},
"fast": {
"message": "तज"
},
@ -1277,6 +1289,9 @@
"functionApprove": {
"message": "फशन: अनित कर"
},
"functionSetApprovalForAll": {
"message": "कय: सभिए सिट कर"
},
"functionType": {
"message": "फशन करकर"
},
@ -1463,7 +1478,7 @@
},
"highGasSettingToolTipMessage": {
"message": "लकपिय NFT डप ज वजह सटवरक टिक मि कवर करनिए $1 क उपयग कर।",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "उचच"
@ -1617,6 +1632,9 @@
"invalidSeedPhrase": {
"message": "अमय गत रिकवरज"
},
"invalidSeedPhraseCaseSensitive": {
"message": "अमय निश! गत पनरिश कस स।"
},
"ipfsGateway": {
"message": "IPFS गटव"
},
@ -1766,7 +1784,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "एक ससमत करतिए $1 क उपयग कर। समय क अनन बहत कम सहिमतछ हद तक अपरतित ह।",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "निन"
@ -1810,7 +1828,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "मर मय पर तस करनिए $1क उपयग कर।",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "म"
@ -1892,6 +1910,19 @@
"message": "नटवरक विवरण सतित कर",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"mismatchedChainRecommendation": {
"message": "हम सििश करति आग बढ पहल आप $1 कर।",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "हमिड क अनर, नटवरक कम इस चन आईडक सल नह सकत।"
},
"mismatchedNetworkSymbol": {
"message": "सबमिट कि गयत इस चन आईडिए हम अपल नह।"
},
"mismatchedRpcUrl": {
"message": "हमिड क अनर, सबमिट कि गय RPC URL मन इस चन आईडित पइडर सल नह।"
},
"missingNFT": {
"message": "अपन NFT नहख रह?"
},
@ -1943,6 +1974,9 @@
"network": {
"message": "नटवरक:"
},
"networkAddedSuccessfully": {
"message": "नटवरक सफलतवक ज गय!"
},
"networkDetails": {
"message": "नटवरक विवरण"
},
@ -2059,6 +2093,9 @@
"message": "नस $1 कए गए नस स अधिक ह",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "एनएफट"
},
"nftTokenIdPlaceholder": {
"message": "सरहणय ID दरज कर"
},
@ -2130,7 +2167,7 @@
},
"notifications10ActionText": {
"message": "सिस म",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "बहतर टकन डिशन वरतमन म Ethereum Mainnet, Polygon, BSC और Avalanche कटवर पर उपलबध ह। और भ आन!"
@ -2154,11 +2191,21 @@
"message": "डक मड सकषम कर"
},
"notifications12Description": {
"message": "नए उपयगकरिए उनकिटम पथमिकत आधर पर डक मड सकषम किएग। म उपयगकरिए, सिग->एकसपिटल कअल रप सक मड सकषम कर।"
"message": "एकसटशन पर डक मड आखिरकर आ गय! इस करनिए, सिग -> पिक पर ज और परदरशन विकल एक क चयन कर: लइट, डक, सिटम।"
},
"notifications12Title": {
"message": "वन डक मड? अब डक मड! 🕶🦊"
},
"notifications13ActionText": {
"message": "कसटम नटवरक सि"
},
"notifications13Description": {
"message": "अब आप निनलिित लकपिय कसटम नटवरस आस सकत: आरिरम, एवलश, बिस सट चन, फटम, हमन, ऑपििम, पम एड पन! इस फचर क एनबल करनिए, सिस ->एकसपिटल पर ज और \"श कसटम नटवरक लिट\" ऑन कर!",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "लकपिय नटवरस ज"
},
"notifications1Description": {
"message": "MetaMask Mobile उपयगकर अब अपनइल वट कदर टकन सप कर सकत। मइल ऐप पत करनिए QR कड कन कर और सप करन कर।",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2225,7 +2272,7 @@
},
"notifications8ActionText": {
"message": "एडवस सिस पर ज",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "MetaMask v10.4.0 क अनर, अब आपक अपनजर डिइस कक स कनट करनिए लजर लइव क आवशयकत नह।",
@ -2255,15 +2302,11 @@
"message": "सचन"
},
"notificationsInfos": {
"message": "$1 $2",
"message": "$1 $2 स",
"description": "$1 is the date at which the notification has been dispatched and $2 is the link to the snap that dispatched the notification."
},
"notificationsMarkAllAsRead": {
"message": "सभ पढआ मक कर"
},
"numberOfNewTokensDetected": {
"message": "इस ख $1 क नए टकन पए गए",
"description": "$1 is the number of new tokens detected"
"message": "सभ पढआ चिित कर"
},
"ofTextNofM": {
"message": "क"
@ -2344,9 +2387,6 @@
"message": "अपनजर क WebHID कयम स कनट करनिए MetaMask कण सन म।",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "सस कड ज"
},
"optional": {
"message": "वकलिक"
},
@ -2473,7 +2513,7 @@
},
"preferredLedgerConnectionType": {
"message": "वरयतजर कनशन करकर",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "सप क कर रह..."
@ -2646,7 +2686,7 @@
"message": "आग बढ पहलिित करि आप सहट रिकवर इसल कर रह। इस आप अनड नह कर प।"
},
"restartMetamask": {
"message": "क कनर कर"
"message": "MetaMask किर स कर"
},
"restore": {
"message": "पनरित कर"
@ -2676,6 +2716,14 @@
"revealTheSeedPhrase": {
"message": "सड फि"
},
"revokeAllTokensTitle": {
"message": "अपन सभ $1 क एकस करन अनमतििरसत कर?",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "अनमतििरसत करन, निनलिित $1 अब आपक $2 क एकस नह कर सक",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Rinkeby टट नटवरक"
},
@ -2852,12 +2900,23 @@
"message": "$1 भ रह",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "चवन: आप एक टकन अनध कजनिसक परिमसवरप धन कि सकत। $1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "एडवस गपनयतिस निित कर"
},
"setAdvancedPrivacySettingsDetails": {
"message": "MetaMask उतद क उपयि और सरक बढिए इन विवसनय तसर-पकष क उपयग करत।"
},
"setApprovalForAll": {
"message": "सभिए सिट कर"
},
"setApprovalForAllTitle": {
"message": "बिि खरच स $1 सत कर",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "सिग"
},
@ -2877,6 +2936,12 @@
"showAdvancedGasInlineDescription": {
"message": "गस मय और सिरण कजन और पि करनन पर दििए इसक चयन कर।"
},
"showCustomNetworkList": {
"message": "कसटम नटवरक कि"
},
"showCustomNetworkListDescription": {
"message": "नयटवरक ज समय पहल भरए विवरण वटवरििए इस।"
},
"showFiatConversionInTestnets": {
"message": "टट नटवरक पर रतरण दि"
},
@ -2967,10 +3032,6 @@
"snapInstallWarningCheck": {
"message": "यि करनिए कि आप समझत, सभ पर सहिन लग।"
},
"snapInstallWarningKeyAccess": {
"message": "आप सप \"$1\" क महतवपण एकस परदन कर रह। यह अपरिवरतनय ह और आपक अकस और एसस पर \"$1\" किरण परदन करत। आग बढ पहलिित करि आप \"$1\" पर भर करत।",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "यप निनलिित अनमति अनध कर रह:"
},
@ -2986,6 +3047,9 @@
"snapsToggle": {
"message": "कई सप तभ चल जब उस सकषम कि गय"
},
"someNetworksMayPoseSecurity": {
"message": "कछ नटवरक सरक और/यपनयतिम प कर सकत। नटवरक ज और उपयग करन पहलि समझ।"
},
"somethingWentWrong": {
"message": "ओह! कछ गलत ह गय।"
},
@ -3164,10 +3228,10 @@
"message": "सट लनदन अनपलबध ह पर भ आप अपनकनप कर सकत।"
},
"stxPendingPrivatelySubmittingSwap": {
"message": "आपकप निप स रह..."
"message": "आपकप निप स सबमिट कि रह..."
},
"stxPendingPubliclySubmittingSwap": {
"message": "आपकप सवजनिक रप स रह..."
"message": "आपकप सवजनिक रप स सबमिट कि रह..."
},
"stxSubDescription": {
"message": "* सट लनदन आपकनदन किर पर, अनक बर जम करनरयस कर। यदि सभरयस विफल ह, तनदन कवजनिक रप सरसित किएगि यह सिित ह सकि आपकप सफलतवक प।"
@ -3180,7 +3244,7 @@
"description": "$1 is a token symbol, e.g. ETH"
},
"stxSwapCompleteIn": {
"message": "स< म",
"message": "सष समय <",
"description": "'<' means 'less than', e.g. Swap will complete in < 2:59"
},
"stxTooltip": {
@ -3548,6 +3612,10 @@
"switchNetworks": {
"message": "नटवरक सिच कर"
},
"switchToNetwork": {
"message": "$1 पर सिच कर",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "इस ख पर सिच कर"
},
@ -3635,7 +3703,7 @@
},
"toggleTestNetworks": {
"message": "$1 परषण नटवरक",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "टकन"
@ -3658,18 +3726,6 @@
"tokenDetails": {
"message": "टकन विवरण"
},
"tokenDetection": {
"message": "टकन डिशन"
},
"tokenDetectionAlertMessage": {
"message": "फिलहल टकन डिशन $1 पर उपलबध ह। $2"
},
"tokenDetectionAnnouncement": {
"message": "नय! पिक फचर कप म Ethereum Mainnet पर बहतर टकन डिशन उपलबध ह। $1"
},
"tokenDetectionToggleDescription": {
"message": "ConsenSys ककन क एपआई वििन थरड पकन सिकन क एक स एकतर करत। इसद करन आपकट म गए नए टकन क पत चलनद हएग, लिन इट करनिए टकन खजनिकलप बन रह।"
},
"tokenId": {
"message": "टकन आइड"
},
@ -3805,7 +3861,7 @@
"description": "$1 is the wallet device name; $2 is a link to wallet connection guide"
},
"troubleStarting": {
"message": "क क करनिकत हई। यह तिक-रक कर ह सकत, इसलिए एकसटशन क नरभ करनरयस कर।"
"message": "MetaMask क करन परई। यह तिक-रक कर ह सकत, इसलिए एकसटशन क िर स करक।"
},
"troubleTokenBalances": {
"message": "हम आपककन कषरिड करन परई। आप उनख सकत ",
@ -3851,6 +3907,9 @@
"unknownCameraErrorTitle": {
"message": "ओह! कछ गलत ह गय...."
},
"unknownCollection": {
"message": "अनम सरह"
},
"unknownNetwork": {
"message": "अजत निटवरक"
},
@ -3901,12 +3960,6 @@
"usePhishingDetectionDescription": {
"message": "Ethereum उपयगकर लकित करनििग डन किए एक चवनरदरित कर"
},
"useTokenDetection": {
"message": "टकन डिशन क उपयग कर"
},
"useTokenDetectionDescription": {
"message": "हम आपकट म गए नए टकन क पत लग और परदरित करनिए तसर-पकष API क उपयग करत। बद कर यदि आप नहहति MetaMask उन सल कर।"
},
"useTokenDetectionPrivacyDesc": {
"message": "आपक गए टकन कवचित रप सरदरित करन थरड प सरवरस कथ सर शिल रह, जकन किम करत। व सरवरस आपक IP एडस क एकस कर प।"
},
@ -3987,6 +4040,9 @@
"walletCreationSuccessTitle": {
"message": "वट किण सफल हआ"
},
"wantToAddThisNetwork": {
"message": "इस नटवरक कहत?"
},
"warning": {
"message": "चवन"
},
@ -4049,6 +4105,10 @@
"yesLetsTry": {
"message": "ह, आइए आजम"
},
"youHaveAddedAll": {
"message": "आपन सभकपिय नटवरक जिए ह। आप अधिक नटवरक खज सकत $1 य आप $2 कर सकत",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "आपक इस सि उपयग करनिए कमर तक पहच क अनमति आवशयकत।"
},

@ -157,6 +157,9 @@
"addMemo": {
"message": "Tambahkan memo"
},
"addMoreNetworks": {
"message": "tambahkan jaringan secara manual"
},
"addNetwork": {
"message": "Tambahkan Jaringan"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "Peringatan"
},
"allOfYour": {
"message": "Seluruh $1 Anda",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "Izinkan ekstensi eksternal ini untuk:"
},
@ -263,6 +270,10 @@
"approve": {
"message": "Setujui batas penggunaan"
},
"approveAllTokensTitle": {
"message": "Berikan izin untuk mengakses seluruh $1 Anda?",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "Setujui & Instal"
},
@ -282,9 +293,6 @@
"approvedAsset": {
"message": "Aset yang disetujui"
},
"areYouDeveloper": {
"message": "Anda seorang pengembang?"
},
"areYouSure": {
"message": "Anda yakin?"
},
@ -438,7 +446,7 @@
"message": "Beli $1 dengan Wyre"
},
"buyWithWyreDescription": {
"message": "Wyre memungkinkan Anda menggunakan kartu debit untuk menyetorkan ETH langsung di akun MetaMask Anda."
"message": "Orientasi mudah untuk pembelian hingga $ 1000. Verifikasi pembelian limit tinggi interaktif yang cepat. Mendukung Kartu Debit/Kredit, Apple Pay, Transfer Bank. Tersedia di 100+ negara. Token disetor ke Akun MetaMask Anda"
},
"bytes": {
"message": "Byte"
@ -466,6 +474,13 @@
"message": "Untuk $1 suatu transaksi, biaya gas harus dinaikkan minimal 10% agar dapat dikenali oleh jaringan.",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "Batalkan swap untuk ~$1",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "Batalkan swap gratis"
},
"cancellationGasFee": {
"message": "Biaya Pembatalan Gas"
},
@ -650,7 +665,7 @@
"message": "Interaksi Kontrak"
},
"convertTokenToNFTDescription": {
"message": "Kami mendeteksi bahwa aset ini merupakan NFT. Kini MetaMask memiliki dukungan asli penuh untuk NFT. Anda ingin menghapusnya dari daftar token dan menambahkannya sebagai NFT?"
"message": "Kami mendeteksi bahwa aset ini merupakan NFT. Kini MetaMask memiliki dukungan asli penuh untuk NFT. Ingin menghapusnya dari daftar token dan menambahkannya sebagai NFT?"
},
"convertTokenToNFTExistDescription": {
"message": "Kami mendeteksi bahwa aset ini telah ditambahkan sebagai NFT. Anda ingin menghapusnya dari daftar token?"
@ -732,7 +747,7 @@
},
"customGasSettingToolTipMessage": {
"message": "Gunakan $1 untuk menyesuaikan harga gas. Anda akan bingung jika tidak terbiasa. Berinteraksi dengan risiko Anda sendiri.",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "Menaikkan biaya dapat mengurangi waktu pemrosesan, namun tidak ada jaminan."
@ -818,7 +833,7 @@
},
"depositCrypto": {
"message": "Deposit $1",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "Deskripsi"
@ -1196,9 +1211,6 @@
"failureMessage": {
"message": "Ada yang salah, dan kami tidak dapat menyelesaikan tindakan"
},
"fakeTokenWarning": {
"message": "Siapa pun dapat membuat token, termasuk membuat versi palsu dari token yang ada. Pelajari selengkapnya seputar $1"
},
"fast": {
"message": "Cepat"
},
@ -1277,6 +1289,9 @@
"functionApprove": {
"message": "Fungsi: Setujui"
},
"functionSetApprovalForAll": {
"message": "Fungsi: SetApprovalForAll"
},
"functionType": {
"message": "Jenis Fungsi"
},
@ -1463,7 +1478,7 @@
},
"highGasSettingToolTipMessage": {
"message": "Gunakan $1 untuk menutupi lonjakan lalu lintas jaringan karena hal-hal seperti penurunan NFT populer.",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "tinggi"
@ -1617,6 +1632,9 @@
"invalidSeedPhrase": {
"message": "Frasa Pemulihan Rahasia Tidak Valid"
},
"invalidSeedPhraseCaseSensitive": {
"message": "Masukan tidak valid! Frasa Pemulihan Rahasia peka terhadap huruf besar/kecil."
},
"ipfsGateway": {
"message": "Gateway IPFS"
},
@ -1766,7 +1784,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "Gunakan $1 untuk menunggu harga yang lebih murah. Estimasi waktu kurang akurat karena harga sedang tidak dapat diprediksi.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "rendah"
@ -1810,7 +1828,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "Gunakan $1 untuk pemrosesan cepat dengan harga pasar saat ini.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "memo"
@ -1892,6 +1910,19 @@
"message": "memverifikasi detail jaringan",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"mismatchedChainRecommendation": {
"message": "Kami menyarankan agar Anda $1 sebelum melanjutkan.",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "Menurut catatan kami, nama jaringan mungkin tidak sepenuhnya sesuai dengan ID rantai ini."
},
"mismatchedNetworkSymbol": {
"message": "Simbol mata uang yang dikirimkan tidak sesuai dengan yang kami harapkan untuk ID rantai ini."
},
"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?"
},
@ -1943,6 +1974,9 @@
"network": {
"message": "Jaringan:"
},
"networkAddedSuccessfully": {
"message": "Jaringan berhasil ditambahkan!"
},
"networkDetails": {
"message": "Detail Jaringan"
},
@ -2059,6 +2093,9 @@
"message": "Nonce lebih tinggi dari nonce $1 yang disarankan",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "NFT"
},
"nftTokenIdPlaceholder": {
"message": "Masukkan ID koleksi"
},
@ -2130,7 +2167,7 @@
},
"notifications10ActionText": {
"message": "Lihat di pengaturan",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "Deteksi token yang ditingkatkan saat ini tersedia di jaringan Ethereum Mainnet, Polygon, BSC, dan Avalanche. Nantikan selengkapnya!"
@ -2154,11 +2191,21 @@
"message": "Aktifkan mode gelap"
},
"notifications12Description": {
"message": "Mode Gelap akan diaktifkan untuk pengguna baru tergantung preferensi sistem mereka. Untuk pengguna lama, aktifkan Mode Gelap secara manual di bawah Pengaturan -> Eksperimental."
"message": "Mode gelap pada Ekstensi akhirnya hadir! Untuk menyalakannya, buka Pengaturan -> Eksperimental dan pilih salah satu opsi tampilan: Terang, Gelap, Sistem."
},
"notifications12Title": {
"message": "Kapan mode gelap? Ini saatnya mode gelap! 🕶🦊"
},
"notifications13ActionText": {
"message": "Tampilkan daftar jaringan khusus"
},
"notifications13Description": {
"message": "Kini Anda dapat menambahkan jaringan khusus populer berikut dengan mudah: Arbitrum, Avalanche, Binance Smart Chain, Fantom, Harmony, Optimism, Palm, dan Polygon! Untuk mengaktifkan fitur ini, buka Pengaturan -> Eksperimental dan aktifkan \"Tampilkan daftar jaringan khusus\"!",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "Tambahkan Jaringan Populer"
},
"notifications1Description": {
"message": "Pengguna MetaMask Mobile kini bisa menukar token di dalam dompet seluler mereka. Pindai kode QR untuk mendapatkan aplikasi seluler dan mulai menukar.",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2225,7 +2272,7 @@
},
"notifications8ActionText": {
"message": "Buka Pengaturan Lanjutan",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "Pada MetaMask v10.4.0, Anda tidak lagi memerlukan Ledger Live untuk menghubungkan perangkat Ledger Anda ke MetaMask.",
@ -2261,10 +2308,6 @@
"notificationsMarkAllAsRead": {
"message": "Tandai semua telah dibaca"
},
"numberOfNewTokensDetected": {
"message": "$1 token baru ditemukan di akun ini",
"description": "$1 is the number of new tokens detected"
},
"ofTextNofM": {
"message": "dari"
},
@ -2344,9 +2387,6 @@
"message": "Buka MetaMask dalam layar penuh untuk menghubungkan ledger Anda melalui WebHID.",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "Periksa kode sumbernya"
},
"optional": {
"message": "Opsional"
},
@ -2473,7 +2513,7 @@
},
"preferredLedgerConnectionType": {
"message": "Jenis Koneksi Ledger Pilihan",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "Mempersiapkan pertukaran..."
@ -2676,6 +2716,14 @@
"revealTheSeedPhrase": {
"message": "Ungkap frasa seed"
},
"revokeAllTokensTitle": {
"message": "Cabut izin untuk mengakses seluruh $1 Anda?",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "Dengan mencabut izin, $1 berikut tidak lagi dapat mengakses $2 Anda",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Jaringan Uji Rinkeby"
},
@ -2852,12 +2900,23 @@
"message": "Mengirim $1",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "Peringatan: Anda akan mengirim kontrak token yang berpotensi mengakibatkan hilangnya dana. $1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "Atur pengaturan privasi lanjutan"
},
"setAdvancedPrivacySettingsDetails": {
"message": "MetaMask menggunakan layanan pihak ketiga tepercaya ini untuk meningkatkan kegunaan dan keamanan produk."
},
"setApprovalForAll": {
"message": "Atur Persetujuan untuk Semua"
},
"setApprovalForAllTitle": {
"message": "Setujui $1 tanpa batas penggunaan",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "Pengaturan"
},
@ -2877,6 +2936,12 @@
"showAdvancedGasInlineDescription": {
"message": "Pilih ini untuk menampilkan biaya gas dan kontrol batas secara langsung di layar kirim dan konfirmasi."
},
"showCustomNetworkList": {
"message": "Tampilkan Daftar Jaringan Khusus"
},
"showCustomNetworkListDescription": {
"message": "Pilih ini untuk menampilkan daftar jaringan dengan detail yang telah diisi saat menambahkan jaringan baru."
},
"showFiatConversionInTestnets": {
"message": "Tampilkan Konversi di Testnet"
},
@ -2967,10 +3032,6 @@
"snapInstallWarningCheck": {
"message": "Untuk mengonfirmasikan Anda sudah paham, centang semua."
},
"snapInstallWarningKeyAccess": {
"message": "Anda memberikan akses kunci ke snap \"$1\". Tindakan ini tidak dapat dibatalkan dan memberikan kendali \"$1\" atas akun dan aset Anda. Sebelum melanjutkan, pastikan \"$1\" aman.",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "Snap ini meminta izin berikut:"
},
@ -2986,6 +3047,9 @@
"snapsToggle": {
"message": "Snap hanya akan beroperasi jika diaktifkan"
},
"someNetworksMayPoseSecurity": {
"message": "Beberapa jaringan dapat menimbulkan risiko keamanan dan/atau privasi. Pahami risikonya sebelum menambahkan & menggunakan jaringan."
},
"somethingWentWrong": {
"message": "Ups! Ada yang salah."
},
@ -3548,6 +3612,10 @@
"switchNetworks": {
"message": "Beralih Jaringan"
},
"switchToNetwork": {
"message": "Beralih ke $1",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "Beralih ke akun ini"
},
@ -3635,7 +3703,7 @@
},
"toggleTestNetworks": {
"message": "$1 jaringan pengujian",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "Token"
@ -3658,18 +3726,6 @@
"tokenDetails": {
"message": "Detail token"
},
"tokenDetection": {
"message": "Deteksi token"
},
"tokenDetectionAlertMessage": {
"message": "Saat ini deteksi token tersedia di $1. $2"
},
"tokenDetectionAnnouncement": {
"message": "Baru! Deteksi token yang ditingkatkan tersedia di Ethereum Mainnet sebagai fitur eksperimental. $1"
},
"tokenDetectionToggleDescription": {
"message": "API token ConsenSys mengumpulkan daftar token dari berbagai daftar token pihak ketiga. Menonaktifkannya akan menghentikan deteksi token baru yang ditambahkan ke dompet Anda, tetapi Anda akan tetap memiliki opsi untuk mencari token yang akan diimpor."
},
"tokenId": {
"message": "ID token"
},
@ -3851,6 +3907,9 @@
"unknownCameraErrorTitle": {
"message": "Ups! Ada yang salah..."
},
"unknownCollection": {
"message": "Koleksi tanpa nama"
},
"unknownNetwork": {
"message": "Jaringan Privat Tidak Dikenal"
},
@ -3901,12 +3960,6 @@
"usePhishingDetectionDescription": {
"message": "Menampilkan peringatan untuk domain pengelabuan yang menargetkan pengguna Ethereum"
},
"useTokenDetection": {
"message": "Gunakan Deteksi Token"
},
"useTokenDetectionDescription": {
"message": "Kami menggunakan API pihak ketiga untuk mendeteksi dan menampilkan token baru yang dikirim ke dompet Anda. Matikan jika Anda tidak ingin MetaMask memakai data dari layanan tersebut."
},
"useTokenDetectionPrivacyDesc": {
"message": "Menampilkan token yang dikirim ke akun Anda secara otomatis yang melibatkan komunikasi dengan server pihak ketiga untuk mengambil gambar token. Server tersebut akan memiliki akses ke alamat IP Anda."
},
@ -3987,6 +4040,9 @@
"walletCreationSuccessTitle": {
"message": "Dompet berhasil dibuat"
},
"wantToAddThisNetwork": {
"message": "Ingin menambahkan jaringan ini?"
},
"warning": {
"message": "Peringatan"
},
@ -4049,6 +4105,10 @@
"yesLetsTry": {
"message": "Ya, mari kita coba"
},
"youHaveAddedAll": {
"message": "Anda telah menambahkan semua jaringan populer. Anda dapat menemukan lebih banyak jaringan $1 atau dapat $2",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "Anda harus mengizinkan akses kamera untuk menggunakan fitur ini."
},

@ -591,6 +591,9 @@
"forgetDevice": {
"message": "Dimentica questo dispositivo"
},
"forgotPassword": {
"message": "Hai dimenticato la password?"
},
"from": {
"message": "Da"
},

@ -157,6 +157,9 @@
"addMemo": {
"message": "メモを追加"
},
"addMoreNetworks": {
"message": "他のネットワークを手動で追加"
},
"addNetwork": {
"message": "ネットワークを追加"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "アラート"
},
"allOfYour": {
"message": "すべての $1",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "この外部拡張機能に次の操作を許可します"
},
@ -263,6 +270,10 @@
"approve": {
"message": "使用限度額の承認"
},
"approveAllTokensTitle": {
"message": "すべての $1 へのアクセスを許可しますか?",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "承認してインストール"
},
@ -282,9 +293,6 @@
"approvedAsset": {
"message": "承認済みのアセット"
},
"areYouDeveloper": {
"message": "開発者の方ですか?"
},
"areYouSure": {
"message": "よろしいですか?"
},
@ -438,7 +446,7 @@
"message": "Wyreで$1を購入"
},
"buyWithWyreDescription": {
"message": "Wyreを使用すると、デビット カードを使用して、$1 をMetaMaskアカウントに直接デポジットできます。"
"message": "簡単なオンボーディングプロセスで最高 $ 1000 購入可能。迅速かつインタラクティブな高限度額の購入検証。デビット・クレジットカード、Apple Pay、銀行送金に対応。100か国以上で利用可能。トークンは MetaMask アカウントに入金されます。"
},
"bytes": {
"message": "バイト"
@ -466,6 +474,13 @@
"message": "トランザクションを$1するには、ネットワークに認識されるようにガス代を 10% 以上増額する必要があります。",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "$1 以下でスワップをキャンセル",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "無料でスワップをキャンセル"
},
"cancellationGasFee": {
"message": "キャンセルのガス代"
},
@ -732,7 +747,7 @@
},
"customGasSettingToolTipMessage": {
"message": "ガス代をカスタマイズするには$1を使用します。慣れていない場合はわかりにくい可能性があります。自己責任で操作してください。",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "手数料を増やすと処理時間は短くなる可能性がありますが、必ずそうなるとは限りません。"
@ -818,7 +833,7 @@
},
"depositCrypto": {
"message": "$1 を入金",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "説明"
@ -1196,9 +1211,6 @@
"failureMessage": {
"message": "問題が発生しました。アクションを完了させることができません"
},
"fakeTokenWarning": {
"message": "既存のトークンの偽のバージョンの作成を含め、誰でもトークンを作成できます。$1に関する詳細をご覧ください"
},
"fast": {
"message": "高速"
},
@ -1277,6 +1289,9 @@
"functionApprove": {
"message": "機能: 承認"
},
"functionSetApprovalForAll": {
"message": "関数: SetApprovalForAll"
},
"functionType": {
"message": "機能の種類"
},
@ -1463,7 +1478,7 @@
},
"highGasSettingToolTipMessage": {
"message": "人気のNFTドロップなどによるネットワークトラフィックの急増に備えるため、$1を使用してください。",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "高"
@ -1617,6 +1632,9 @@
"invalidSeedPhrase": {
"message": "無効なシークレットリカバリーフレーズ"
},
"invalidSeedPhraseCaseSensitive": {
"message": "入力値が無効です!秘密のリカバリーフレーズは大文字・小文字が区別されます。"
},
"ipfsGateway": {
"message": "IPFSゲートウェイ"
},
@ -1766,7 +1784,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "値下がりを待つには$1を使用してください。価格がやや予測不能なため、予想時間はあまり正確ではありません。",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "低"
@ -1810,7 +1828,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "現在の市場価格での迅速な処理には、$1を使用してください。",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "メモ"
@ -1892,6 +1910,19 @@
"message": "ネットワークの詳細の確認",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"mismatchedChainRecommendation": {
"message": "先に進む前に$1をお勧めします。",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "弊社の記録によると、ネットワーク名がこのチェーン ID と正しく一致していない可能性があります。"
},
"mismatchedNetworkSymbol": {
"message": "送信された通貨記号がこのチェーン ID に関して予想されるものと一致していません。"
},
"mismatchedRpcUrl": {
"message": "弊社の記録によると、送信された RPC URL の値がこのチェーン ID の既知のプロバイダーと一致しません。"
},
"missingNFT": {
"message": "NFTが見当たりませんか?"
},
@ -1943,6 +1974,9 @@
"network": {
"message": "ネットワーク:"
},
"networkAddedSuccessfully": {
"message": "ネットワークが追加されました!"
},
"networkDetails": {
"message": "ネットワークの詳細"
},
@ -2059,6 +2093,9 @@
"message": "ナンスが提案され$1よりも大きいです",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "NFT"
},
"nftTokenIdPlaceholder": {
"message": "コレクティブルIDを入力してください"
},
@ -2130,7 +2167,7 @@
},
"notifications10ActionText": {
"message": "設定に移動",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "改善されたトークン検出は、現在 Ethereum Mainnet、Polygon、BSC、Avalanche ネットワークで利用できます。他のネットワークも追加される予定です!"
@ -2154,11 +2191,21 @@
"message": "ダークモードを有効にする"
},
"notifications12Description": {
"message": "新規ユーザーの場合、システム設定に従ってダークモードが有効になります。既存のユーザーは、設定 -> 実験的機能で、ダークモードを手動で有効にできます。"
"message": "拡張機能のダークモードがついに追加されました!オンにするには、設定 - 実験的機能の順に移動し、ライト、ダーク、システムの表示オプションから一つを選択してください。"
},
"notifications12Title": {
"message": "いつダークモードに?今ダークモードです!🕶🦊"
},
"notifications13ActionText": {
"message": "カスタムネットワークリストを表示"
},
"notifications13Description": {
"message": "人気のカスタムネットワーク(Arbitrum、Avalanche、Binance Smart Chain、Fantom、Harmony、Optimism、Palm、Polygon)が簡単に追加できるようになりました!この機能を有効にするには、設定 -> 実験的機能に移動し、「カスタムネットワークリストを表示」をオンにしてください!",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "人気のネットワークを追加"
},
"notifications1Description": {
"message": "MetaMask Mobileのユーザーが、モバイルウォレット内でトークンを交換できるようになりました。QRコードをスキャンしてモバイルアプリを取得し、スワップを開始します。",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2225,7 +2272,7 @@
},
"notifications8ActionText": {
"message": "高度な設定に移動",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "MetaMask v10.4.0以降では、LedgerデバイスのMetaMaskへの接続にLedger Liveが不要になりました。",
@ -2261,10 +2308,6 @@
"notificationsMarkAllAsRead": {
"message": "すべて既読にする"
},
"numberOfNewTokensDetected": {
"message": "$1 の新しいトークンがこのアカウントで見つかりました",
"description": "$1 is the number of new tokens detected"
},
"ofTextNofM": {
"message": "中の"
},
@ -2344,9 +2387,6 @@
"message": "WebHIDでLedgerを接続するには、MetaMaskを全画面モードで開いてください。",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "ソースコードを確認"
},
"optional": {
"message": "任意"
},
@ -2473,7 +2513,7 @@
},
"preferredLedgerConnectionType": {
"message": "優先Ledger接続タイプ",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "スワップを準備しています..."
@ -2676,6 +2716,14 @@
"revealTheSeedPhrase": {
"message": "シードフレーズを表示"
},
"revokeAllTokensTitle": {
"message": "すべての $1 へのアクセス許可を取り消しますか?",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "アクセス許可を取り消すと、次の $1 が今後 $2 にアクセスできなくなります",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Rinkebyテストネットワーク"
},
@ -2852,12 +2900,23 @@
"message": "$1を送信中",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "警告: 資金の喪失に繋がる可能性のあるトークンコントラクトに送信しようとしています。$1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "高度なプライバシー設定を設定"
},
"setAdvancedPrivacySettingsDetails": {
"message": "MetaMaskはこれらの信頼できるサードパーティーサービスを使用して、製品の使いやすさと安全性を向上させています。"
},
"setApprovalForAll": {
"message": "すべてを承認に設定"
},
"setApprovalForAllTitle": {
"message": "使用限度額なしで $1 を承認",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "設定"
},
@ -2877,6 +2936,12 @@
"showAdvancedGasInlineDescription": {
"message": "これを選択すると、ガス代と限度額のコントロールが送金画面と確認画面に直接表示されます。"
},
"showCustomNetworkList": {
"message": "カスタムネットワークリストを表示"
},
"showCustomNetworkListDescription": {
"message": "新規ネットワークの追加時に事前に情報が入力済みのネットワークのリストを表示するには、これを選択します。"
},
"showFiatConversionInTestnets": {
"message": "テストネット上に変換を表示"
},
@ -2967,10 +3032,6 @@
"snapInstallWarningCheck": {
"message": "理解したことを確認するために、すべての項目にチェックを入れてください。"
},
"snapInstallWarningKeyAccess": {
"message": "スナップ「$1」に重要なアクセス権を付与しようとしています。これは取り消し不可能で、「$1」によるアカウントとアセットのコントロールが可能になります。続行する前に、「$1」が信頼できることを確認してください。",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "このスナップが次のパーミッションをリクエストしています:"
},
@ -2986,6 +3047,9 @@
"snapsToggle": {
"message": "スナップは有効になっている場合にのみ実行されます"
},
"someNetworksMayPoseSecurity": {
"message": "ネットワークによっては、セキュリティやプライバシーの面でリスクが伴う可能性があります。ネットワークを追加・使用する前にリスクを理解するようにしてください。"
},
"somethingWentWrong": {
"message": "申し訳ありません。問題が発生しました。"
},
@ -3548,6 +3612,10 @@
"switchNetworks": {
"message": "ネットワークを切り替える"
},
"switchToNetwork": {
"message": "$1 に切り替える",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "このアカウントに切り替える"
},
@ -3635,7 +3703,7 @@
},
"toggleTestNetworks": {
"message": "$1テストネットワーク",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "トークン"
@ -3658,18 +3726,6 @@
"tokenDetails": {
"message": "トークンの詳細"
},
"tokenDetection": {
"message": "トークン検出"
},
"tokenDetectionAlertMessage": {
"message": "トークン検出は現在 $1 で利用可能です。$2"
},
"tokenDetectionAnnouncement": {
"message": "新機能! 実験的な機能として、Ethereum Mainnetでのトークン検出が改善されました。$1"
},
"tokenDetectionToggleDescription": {
"message": "ConsenSys のトークン API は、さまざまなサードパーティのトークンリストからトークンのリストを集積します。これをオフにすると、ウォレットに追加された新しいトークンは検出されなくなりますが、引き続きトークンを検索してインポートすることは可能です。"
},
"tokenId": {
"message": "トークン ID"
},
@ -3851,6 +3907,9 @@
"unknownCameraErrorTitle": {
"message": "申し訳ありません。問題が発生しました..."
},
"unknownCollection": {
"message": "無名のコレクション"
},
"unknownNetwork": {
"message": "不明なプライベートネットワーク"
},
@ -3901,12 +3960,6 @@
"usePhishingDetectionDescription": {
"message": "イーサリアムユーザーを対象としたドメインのフィッシングに対して警告を表示します"
},
"useTokenDetection": {
"message": "トークン検出を使用"
},
"useTokenDetectionDescription": {
"message": "弊社はユーザーのウォレットに送信された新しいトークンを検出して表示するために、サードパーティーAPIを使用します。MetaMaskにこれらのサービスからデータを取得させたくない場合は、この機能をオフにしてください。"
},
"useTokenDetectionPrivacyDesc": {
"message": "アカウントに送られたトークンを自動的に表示するには、サードパーティーサーバーと通信し、トークンの画像を取得する必要があります。これらのサーバーはユーザーの IP アドレスにアクセスできます。"
},
@ -3987,6 +4040,9 @@
"walletCreationSuccessTitle": {
"message": "ウォレットが作成されました"
},
"wantToAddThisNetwork": {
"message": "このネットワークを追加しますか?"
},
"warning": {
"message": "警告"
},
@ -4049,11 +4105,15 @@
"yesLetsTry": {
"message": "はい、やってみます"
},
"youHaveAddedAll": {
"message": "すべての人気ネットワークを追加しました。$1で他のネットワークを発見するか、$2できます。",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "この機能を使用するには、カメラへのアクセスを許可する必要があります。"
},
"youSign": {
"message": "名しています"
"message": "名しています"
},
"yourPrivateSeedPhrase": {
"message": "秘密のシークレットリカバリーフレーズ"

@ -157,6 +157,9 @@
"addMemo": {
"message": "메모 추가"
},
"addMoreNetworks": {
"message": "네트워크 직접 추가"
},
"addNetwork": {
"message": "네트워크 추가"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "경고"
},
"allOfYour": {
"message": "내 $1 모두",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "이 외부 확장을 통해 다음을 허용:"
},
@ -263,6 +270,10 @@
"approve": {
"message": "지출 한도 승인"
},
"approveAllTokensTitle": {
"message": "내 모든 $1에 액세스할 수 있는 권한을 부여할까요?",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "승인 및 설치"
},
@ -282,9 +293,6 @@
"approvedAsset": {
"message": "승인된 자산"
},
"areYouDeveloper": {
"message": "개발자이신가요?"
},
"areYouSure": {
"message": "확실한가요?"
},
@ -438,7 +446,7 @@
"message": "Wyre로 $1 구매"
},
"buyWithWyreDescription": {
"message": "Wyre를 사용하면 체크카드를 이용하여 $1 를 MetaMask 계정에 바로 예치할 수 있습니다."
"message": "최대 $ 1000 구매까지 간편한 온보딩. 신속한 대화형 상한 구매 확인. 직불/신용 카드, Apple Pay, 은행 송금 지원. 100여국 이상에서 사용 가능. MetaMask 계정으로 토큰 입금"
},
"bytes": {
"message": "바이트"
@ -466,6 +474,13 @@
"message": "거래를 $1하려면 가스비를 최소 10%를 인상해야 네트워크에서 인식될 수 있습니다.",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "~$1 비용으로 스왑 취소",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "무료로 스왑 취소"
},
"cancellationGasFee": {
"message": "가스 수수료 취소"
},
@ -732,7 +747,7 @@
},
"customGasSettingToolTipMessage": {
"message": "$1을(를) 사용하여 가스 가격을 맞춤설정하세요. 익숙하지 않은 경우 혼동될 수 있습니다. 자신의 책임하에 상호 작용하세요.",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "수수료를 올리면 처리 시간이 단축되기도 하지만 항상 그렇지는 않습니다."
@ -818,7 +833,7 @@
},
"depositCrypto": {
"message": "$1 입금",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "설명"
@ -1196,9 +1211,6 @@
"failureMessage": {
"message": "문제가 발생했습니다. 작업을 완료할 수 없습니다."
},
"fakeTokenWarning": {
"message": "기존 토큰의 가짜 버전 생성을 포함하여 누구나 토큰을 생성할 수 있습니다. $1에 대해 자세히 알아보기"
},
"fast": {
"message": "빠름"
},
@ -1277,6 +1289,9 @@
"functionApprove": {
"message": "기능: 승인"
},
"functionSetApprovalForAll": {
"message": "기능: 모두승인설정"
},
"functionType": {
"message": "기능 유형"
},
@ -1463,7 +1478,7 @@
},
"highGasSettingToolTipMessage": {
"message": "인기 있는 NFT의 하락 등으로 인한 네트워크 트래픽 급증을 커버하려면 $1을 사용하세요.",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "높음"
@ -1617,6 +1632,9 @@
"invalidSeedPhrase": {
"message": "잘못된 비밀 복구 구문"
},
"invalidSeedPhraseCaseSensitive": {
"message": "입력 오류! 비밀 복구 구문은 대소문자를 구분해야 합니다."
},
"ipfsGateway": {
"message": "IPFS 게이트웨이"
},
@ -1766,7 +1784,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "$1 사용을 통해 더 저렴한 가격을 기다리세요. 가격 예측이 힘들기 때문에 시간 추정은 더욱 부정확합니다.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "낮음"
@ -1810,7 +1828,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "현재 시장 가격으로 빠르게 처리할 수 있도록 $1을(를) 사용하세요.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "메모"
@ -1892,6 +1910,19 @@
"message": "네트워크 세부 정보 검증",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"mismatchedChainRecommendation": {
"message": "계속 진행하기 전에 $1 확인을 권합니다.",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "기록에 따르면 네트워크 이름이 이 체인 ID와 일치하지 않는 것 같습니다."
},
"mismatchedNetworkSymbol": {
"message": "제출하신 환율 기호가 이 체인 ID의 환율과 일치하지 않습니다."
},
"mismatchedRpcUrl": {
"message": "기록에 따르면 제출하신 RPC URL 값이 이 체인 ID에 대해 알려진 공급업체와 일치하지 않습니다."
},
"missingNFT": {
"message": "NFT가 보이지 않나요?"
},
@ -1943,6 +1974,9 @@
"network": {
"message": "네트워크:"
},
"networkAddedSuccessfully": {
"message": "성공적으로 네트워크를 추가했습니다!"
},
"networkDetails": {
"message": "네트워크 세부 정보"
},
@ -2059,6 +2093,9 @@
"message": "임시값이 권장 임시값인 $1보다 큽니다.",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "NFT\n"
},
"nftTokenIdPlaceholder": {
"message": "수집 가능한 ID를 입력하세요."
},
@ -2130,7 +2167,7 @@
},
"notifications10ActionText": {
"message": "설정으로 이동하기",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "향상된 토큰 감지 기능을 현재 이더리움 메인넷과 Polygon, BSC, Avalanche 네트워크에서 이용할 수 있습니다. 더 많은 기능이 준비 중입니다!"
@ -2154,11 +2191,21 @@
"message": "다크모드 활성화"
},
"notifications12Description": {
"message": "신규 사용자의 경우 시스템 설정에 따라 다크모드가 활성화됩니다. 기존의 사용자는 설정 -> 실험에서 직접 다크 모드를 활성화해야 합니다."
"message": "다크모드가 마침내 활성화되었습니다! 설정(Settings) -> 시험 기능(Experimental)으로 이동하여 라이트, 다크, 시스템 중 선택하세요."
},
"notifications12Title": {
"message": "다크모드를 원하세요? 이제 다크모드를 사용하세요! 🕶🦊"
},
"notifications13ActionText": {
"message": "사용자 정의 네트워크 목록 보기"
},
"notifications13Description": {
"message": "이제 Arbitrum, Avalanche, Binance Smart Chain, Fantom, Harmony, Optimism, Palm 및 Polygon과 같은 인기 있는 사용자 정의 네트워크를 쉽게 추가할 수 있습니다! 이 기능을 활성화하려면 설정 -> 실험으로 이동하여 \"사용자 지정 네트워크 목록 표시\"를 켜세요!",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "인기 네트워크 추가"
},
"notifications1Description": {
"message": "MetaMask 모바일 사용자는 이제 모바일 지갑에서 토큰을 스왑할 수 있습니다. QR 코드를 스캔하여 모바일 앱을 설치하고 스왑을 시작하세요.",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2225,7 +2272,7 @@
},
"notifications8ActionText": {
"message": "고급 설정으로 이동하기",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "MetaMask v10.4.0부터는 Ledger 장치를 MetaMask에 연결할 때 더 이상 Ledger Live를 사용하지 않아도 됩니다.",
@ -2261,10 +2308,6 @@
"notificationsMarkAllAsRead": {
"message": "모두 읽음으로 표시"
},
"numberOfNewTokensDetected": {
"message": "계정에서 $1개의 새로운 토큰이 발견됨",
"description": "$1 is the number of new tokens detected"
},
"ofTextNofM": {
"message": "/"
},
@ -2344,9 +2387,6 @@
"message": "전체 화면에서 MetaMask를 열어 WebHID를 통해 Ledger를 연결합니다.",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "소스 코드를 확인하세요"
},
"optional": {
"message": "옵션"
},
@ -2473,7 +2513,7 @@
},
"preferredLedgerConnectionType": {
"message": "선호하는 Ledger 연결 유형",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "스왑 준비 중..."
@ -2676,6 +2716,14 @@
"revealTheSeedPhrase": {
"message": "시드 구문 보기"
},
"revokeAllTokensTitle": {
"message": "내 모든 $1에 액세스할 수 있는 권한을 취소할까요?",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "권한을 취소하면 다음 $1의 $2 권한은 더 이상 유효하지 않습니다",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Rinkeby 테스트 네트워크"
},
@ -2852,12 +2900,23 @@
"message": "$1 보내기",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "경고: 토큰 주소를 전송하면 토큰이 손실될 수 있습니다. $1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "개인정보 설정 고급 지정"
},
"setAdvancedPrivacySettingsDetails": {
"message": "이와 같이 MetaMask는 신용있는 타사의 서비스를 사용하여 제품 가용성과 안전성을 향상합니다."
},
"setApprovalForAll": {
"message": "모두 승인 설정"
},
"setApprovalForAllTitle": {
"message": "$1 무제한 지출 승인",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "설정"
},
@ -2877,6 +2936,12 @@
"showAdvancedGasInlineDescription": {
"message": "이 항목을 선택하면 보내기 및 확인 화면에서 바로 가스 가격과 한도 조절을 확인할 수 있습니다."
},
"showCustomNetworkList": {
"message": "사용자 정의 네트워크 보기"
},
"showCustomNetworkListDescription": {
"message": "이를 선택하면 새로 네트워크를 추가할 때 네트워크 목록에 상세 설명이 함께 나타납니다."
},
"showFiatConversionInTestnets": {
"message": "테스트넷에 전환 표시"
},
@ -2967,10 +3032,6 @@
"snapInstallWarningCheck": {
"message": "이해하셨으면 모두 체크해 주세요."
},
"snapInstallWarningKeyAccess": {
"message": "'$1' 스냅 이용에 필요한 키 액세스 권한을 부여하고 있습니다. 이 작업은 사용자의 계정과 자산에 '$1' 제어 권한을 부여하며 취소가 불가능합니다. '$1의 신뢰성을 확인한 후에 진행하세요.",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "이 스냅이 다음 권한을 요청하고 있습니다."
},
@ -2986,6 +3047,9 @@
"snapsToggle": {
"message": "스냅은 활성화된 상태에서만 작동합니다."
},
"someNetworksMayPoseSecurity": {
"message": "네트워크에 따라 보안이나 개인 정보 유출의 위험이 있을 수 있습니다. 네트워크 추가 및 사용 이전에 위험 요소를 파악하세요."
},
"somethingWentWrong": {
"message": "죄송합니다! 문제가 생겼습니다."
},
@ -3548,6 +3612,10 @@
"switchNetworks": {
"message": "네트워크 전환"
},
"switchToNetwork": {
"message": "$1 네트워크로 전환",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "이 계정으로 전환"
},
@ -3635,7 +3703,7 @@
},
"toggleTestNetworks": {
"message": "$1 테스트 네트워크",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "토큰"
@ -3658,18 +3726,6 @@
"tokenDetails": {
"message": "토큰 상세 정보"
},
"tokenDetection": {
"message": "토큰 감지"
},
"tokenDetectionAlertMessage": {
"message": "토큰 감지 기능은 현재 $1. $2에서 사용할 수 있습니다."
},
"tokenDetectionAnnouncement": {
"message": "신규! 개선된 토큰 감지는 실험적 기능으로 이더리움 메인넷에서 사용할 수 있습니다. $1"
},
"tokenDetectionToggleDescription": {
"message": "ConsenSys의 토큰 API는 타사의 다양한 목록을 모아 하나의 토큰 목록을 작성합니다. 이 API를 끄면 신규 토큰을 감지해서 지갑에 추가하는 기능은 중단되지만 토큰을 검색해서 가져오는 기능은 계속 사용할 수 있습니다."
},
"tokenId": {
"message": "토큰 ID"
},
@ -3851,6 +3907,9 @@
"unknownCameraErrorTitle": {
"message": "죄송합니다! 문제가 생겼습니다...."
},
"unknownCollection": {
"message": "제목 미지정 콜렉션"
},
"unknownNetwork": {
"message": "알 수 없는 비공개 네트워크"
},
@ -3901,12 +3960,6 @@
"usePhishingDetectionDescription": {
"message": "이더리움 사용자를 노리는 피싱 도메인에 대한 경고를 표시합니다"
},
"useTokenDetection": {
"message": "토큰 감지 사용"
},
"useTokenDetectionDescription": {
"message": "당사는 타사 API를 사용하여 지갑으로 전송된 새 토큰을 감지하고 표시합니다. MetaMask가 해당 서비스에서 데이터를 가져오는 것을 원하지 않으면 이 기능을 사용하지 마세요."
},
"useTokenDetectionPrivacyDesc": {
"message": "계정으로 전송된 토큰이 자동으로 표시되도록 하려면 타사 서버와의 통신을 통해 토큰 이미지를 불러와야 합니다. 이를 위해 타사 서버는 사용자의 IP 주소에 액세스하게 됩니다."
},
@ -3962,7 +4015,7 @@
"description": "$1 is the action type. e.g (Account, Transaction, Swap)"
},
"visitWebSite": {
"message": "당사 웹사이트 방문하기"
"message": "웹사이트를 방문하세요"
},
"walletConnectionGuide": {
"message": "당사의 하드웨어 지갑 연결 가이드"
@ -3987,6 +4040,9 @@
"walletCreationSuccessTitle": {
"message": "지갑 생성 성공"
},
"wantToAddThisNetwork": {
"message": "이 네트워크를 추가할까요?"
},
"warning": {
"message": "경고"
},
@ -4049,6 +4105,10 @@
"yesLetsTry": {
"message": "예, 시도하겠습니다."
},
"youHaveAddedAll": {
"message": "모든 인기 네트워크를 추가했습니다. $1에서 더 많은 네트워크를 확인하거나 $2 할 수 있습니다.",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "이 기능을 사용하려면 카메라 액세스를 허용해야 합니다."
},

@ -2216,7 +2216,7 @@
"message": "Welcome sa MetaMask"
},
"welcomeBack": {
"message": "Welcome Back!"
"message": "Welcome back!"
},
"whatsNew": {
"message": "Ano'ng bago",

@ -157,6 +157,9 @@
"addMemo": {
"message": "Adicionar observação"
},
"addMoreNetworks": {
"message": "adicionar mais redes manualmente"
},
"addNetwork": {
"message": "Adicionar rede"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "Alertas"
},
"allOfYour": {
"message": "Todos os seus $1",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "Permitir que essa extensão externa:"
},
@ -263,6 +270,10 @@
"approve": {
"message": "Aprovar"
},
"approveAllTokensTitle": {
"message": "Dar permissão para acessar todos os seus $1?",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "Aprovar e instalar"
},
@ -282,9 +293,6 @@
"approvedAsset": {
"message": "Ativo aprovado"
},
"areYouDeveloper": {
"message": "Você é desenvolvedor?"
},
"areYouSure": {
"message": "Tem certeza?"
},
@ -435,10 +443,10 @@
"description": "$1 represents the crypto symbol to be purchased"
},
"buyWithWyre": {
"message": "Comprar $1 com Wyre"
"message": "Comprar $1 com o Wyre"
},
"buyWithWyreDescription": {
"message": "Com o Wyre, você pode usar um cartão de débito para depositar $1 diretamente na sua conta da MetaMask."
"message": "Integração fácil para compras de até US$ 1.000. Verificação de compra de alto limite rápida e interativa. Aceita cartão de crédito/débito, Apple Pay, transferências bancárias. Disponível em mais de 100 países. Depósito de tokens em sua conta na MetaMask"
},
"bytes": {
"message": "Bytes"
@ -466,6 +474,13 @@
"message": "Para $1 uma transação, a taxa de gás deve ser aumentada em pelo menos 10% para que seja reconhecida pela rede.",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "Cancelar swap por ~$1",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "Cancelar swap gratuitamente"
},
"cancellationGasFee": {
"message": "Taxa de gás por cancelamento"
},
@ -650,7 +665,7 @@
"message": "Interação com contrato"
},
"convertTokenToNFTDescription": {
"message": "Detectamos que esse ativo é um NFT. A MetaMask agora oferece suporte nativo a NFTs. Gostaria de removê-lo da sua lista de tokens e adicioná-lo como NFT?"
"message": "Detectamos que esse ativo é um NFT. A MetaMask agora oferece suporte nativo a NFTs. Gostaria de removê-lo de sua lista de tokens e adicioná-lo como NFT?"
},
"convertTokenToNFTExistDescription": {
"message": "Detectamos que esse ativo foi adicionado como NFT. Deseja removê-lo da sua lista de tokens?"
@ -732,7 +747,7 @@
},
"customGasSettingToolTipMessage": {
"message": "Use $1 para personalizar o preço do gás. Isso pode parecer confuso se você não estiver familiarizado. Interaja por sua conta e risco.",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "Aumentar a taxa pode diminuir o tempo de processamento, mas isso não é garantido."
@ -818,7 +833,7 @@
},
"depositCrypto": {
"message": "Depositar $1",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "Descrição"
@ -1196,9 +1211,6 @@
"failureMessage": {
"message": "Ocorreu algum erro e não conseguimos concluir a ação"
},
"fakeTokenWarning": {
"message": "Qualquer um pode criar um token, incluindo versões falsas de tokens existentes. Saiba mais sobre $1"
},
"fast": {
"message": "Rápido"
},
@ -1277,6 +1289,9 @@
"functionApprove": {
"message": "Função: aprovar"
},
"functionSetApprovalForAll": {
"message": "Função: SetApprovalForAll"
},
"functionType": {
"message": "Tipo de função"
},
@ -1463,7 +1478,7 @@
},
"highGasSettingToolTipMessage": {
"message": "Alta probabilidade, mesmo em mercados voláteis. Use $1 para cobrir picos no tráfego de rede em razão de coisas como quedas em NFTs populares.",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "alta"
@ -1617,6 +1632,9 @@
"invalidSeedPhrase": {
"message": "Frase Secreta de Recuperação inválida"
},
"invalidSeedPhraseCaseSensitive": {
"message": "Entrada inválida! A frase secreta de recuperação diferencia maiúsculas e minúsculas."
},
"ipfsGateway": {
"message": "Gateway IPFS"
},
@ -1766,7 +1784,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "Use $1 para aguardar um preço mais baixo. As estimativas de tempo são muito menos exatas, pois os preços são relativamente imprevisíveis.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "baixa"
@ -1810,7 +1828,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "Use $1 para um processamento rápido pelo preço atual de mercado.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "memorando"
@ -1892,6 +1910,19 @@
"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."
},
"mismatchedChainRecommendation": {
"message": "Recomendamos que você $1 antes de prosseguir.",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "De acordo com os nossos registros, o nome da rede pode não corresponder à ID desta cadeia."
},
"mismatchedNetworkSymbol": {
"message": "O símbolo de moeda enviado não corresponde ao esperado para a ID desta cadeia."
},
"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?"
},
@ -1943,6 +1974,9 @@
"network": {
"message": "Ethereum:"
},
"networkAddedSuccessfully": {
"message": "Rede adicionada com sucesso!"
},
"networkDetails": {
"message": "Detalhes da rede"
},
@ -2059,6 +2093,9 @@
"message": "Nonce é maior que o nonce sugerido de $1",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "NFT"
},
"nftTokenIdPlaceholder": {
"message": "Insira o ID do token"
},
@ -2130,7 +2167,7 @@
},
"notifications10ActionText": {
"message": "Veja em configurações",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "A detecção aperfeiçoada de tokens está atualmente disponível nas redes Ethereum Mainnet, Polygon, BSC e Avalanche. Há outras por vir!"
@ -2154,11 +2191,21 @@
"message": "Ativar o modo escuro"
},
"notifications12Description": {
"message": "O Modo Escuro será ativado para novos usuários de acordo com suas preferências de sistema. Para usuários antigos, ative o Modo Escuro manualmente em Configurações -> Experimental."
"message": "O modo escuro na extensão finalmente chegou! Para ativá-lo, acesse Configurações -> Experimental e selecione uma das opções de exibição: claro, escuro, sistema."
},
"notifications12Title": {
"message": "Modo escuro quando? Modo escuro agora! 🕶🦊"
},
"notifications13ActionText": {
"message": "Exibir lista de redes personalizadas"
},
"notifications13Description": {
"message": "Agora você pode adicionar facilmente as seguintes redes personalizadas que são populares: Arbitrum, Avalanche, Binance Smart Chain, Fantom, Harmony, Optimism, Palm e Polygon. Para ativar esse recurso, acesse Configurações -> Experimentais e ative \"Exibir lista de redes personalizadas\".",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "Adicionar redes populares"
},
"notifications1Description": {
"message": "Usuários da MetaMask Mobile agora podem trocar tokens dentro de sua carteira mobile. Leia o QR code para obter o aplicativo para dispositivos móveis e comece a trocar.",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2225,7 +2272,7 @@
},
"notifications8ActionText": {
"message": "Ir para Configurações Avançadas",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "A partir da MetaMask v10.4.0, não é mais necessário o Ledger Live para conectar o seu dispositivo Ledger à MetaMask.",
@ -2261,10 +2308,6 @@
"notificationsMarkAllAsRead": {
"message": "Marcar todas como lidas"
},
"numberOfNewTokensDetected": {
"message": "$1 novos tokens encontrados nesta conta",
"description": "$1 is the number of new tokens detected"
},
"ofTextNofM": {
"message": "de"
},
@ -2344,9 +2387,6 @@
"message": "Abra a MetaMask em tela cheia para conectar sua ledger por meio do WebHID.",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "Verifique o código-fonte"
},
"optional": {
"message": "Opcional"
},
@ -2473,7 +2513,7 @@
},
"preferredLedgerConnectionType": {
"message": "Tipo de conexão preferencial com o Ledger",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "Preparando swap..."
@ -2676,6 +2716,14 @@
"revealTheSeedPhrase": {
"message": "Revelar a frase de recuperação"
},
"revokeAllTokensTitle": {
"message": "Revogar permissão de acesso a todos os seus $1?",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "Ao revogar a permissão, o $1 a seguir não terá mais acesso ao seu $2",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Rede de Teste Rinkeby"
},
@ -2852,12 +2900,23 @@
"message": "Enviando $1",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "Aviso: você está prestes a enviar a um contrato de token que pode resultar em perda de fundos. $1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "Definir configurações avançadas de privacidade"
},
"setAdvancedPrivacySettingsDetails": {
"message": "A MetaMask utiliza esses serviços terceirizados de confiança para aumentar a usabilidade e a segurança dos produtos."
},
"setApprovalForAll": {
"message": "Definir aprovação para todos"
},
"setApprovalForAllTitle": {
"message": "Aprovar $1 sem limite de gastos",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "Definições"
},
@ -2877,6 +2936,12 @@
"showAdvancedGasInlineDescription": {
"message": "Selecione isso para mostrar o preço do gás e limitar os controles diretamente nas telas de envio e de confirmação."
},
"showCustomNetworkList": {
"message": "Exibir lista de redes personalizadas"
},
"showCustomNetworkListDescription": {
"message": "Selecione esta opção para exibir uma lista de redes com as informações pré-preenchidas ao adicionar uma nova rede."
},
"showFiatConversionInTestnets": {
"message": "Mostrar conversão nas redes de teste"
},
@ -2967,10 +3032,6 @@
"snapInstallWarningCheck": {
"message": "Para confirmar que você entende, marque todas."
},
"snapInstallWarningKeyAccess": {
"message": "Você está concedendo ao snap \"$1\" acesso à sua chave. Isso é irrevogável e concede a \"$1\" controle sobre suas contas e ativos. Certifique-se de que confia em \"$1\" antes de prosseguir.",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "Esse snap está solicitando as seguintes permissões:"
},
@ -2986,6 +3047,9 @@
"snapsToggle": {
"message": "O snap só será executado se estiver ativado"
},
"someNetworksMayPoseSecurity": {
"message": "Algumas redes podem representar riscos de segurança e/ou privacidade. Tenha os riscos em mente antes de adicionar e usar uma rede."
},
"somethingWentWrong": {
"message": "Ops! Algo deu errado."
},
@ -3548,6 +3612,10 @@
"switchNetworks": {
"message": "Trocar redes"
},
"switchToNetwork": {
"message": "Trocar para $1",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "Trocar para esta conta"
},
@ -3635,7 +3703,7 @@
},
"toggleTestNetworks": {
"message": "$1 redes de teste",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "Token"
@ -3658,18 +3726,6 @@
"tokenDetails": {
"message": "Dados do token"
},
"tokenDetection": {
"message": "Detecção de token"
},
"tokenDetectionAlertMessage": {
"message": "A detecção de tokens está atualmente disponível em $1. $2"
},
"tokenDetectionAnnouncement": {
"message": "Novidade! A detecção aprimorada de token está disponível na Mainnet do Ethereum como uma funcionalidade experimental. $1"
},
"tokenDetectionToggleDescription": {
"message": "A API de token da ConsenSys agrega uma lista de tokens de várias listas de tokens de terceiros. Se você desativá-la, não haverá detecção de novos tokens adicionados à sua carteira, mas continuará com a opção de procurar tokens para importar."
},
"tokenId": {
"message": "ID do token"
},
@ -3851,6 +3907,9 @@
"unknownCameraErrorTitle": {
"message": "Ops! Algo deu errado...."
},
"unknownCollection": {
"message": "Coleção sem nome"
},
"unknownNetwork": {
"message": "Rede Privada Desconhecida"
},
@ -3901,12 +3960,6 @@
"usePhishingDetectionDescription": {
"message": "Exibir uma advertência para os domínios de phishing destinados a usuários do Ethereum"
},
"useTokenDetection": {
"message": "Usar detecção de tokens"
},
"useTokenDetectionDescription": {
"message": "Utilizamos APIs terceirizadas para detectar e exibir novos tokens enviados à sua carteira. Desative essa opção se não deseja que a MetaMask extraia dados desses serviços."
},
"useTokenDetectionPrivacyDesc": {
"message": "A exibição automática de tokens enviados para a sua conta envolve a comunicação com servidores de terceiros para buscar as imagens dos tokens. Esses servidores terão acesso ao seu endereço IP."
},
@ -3962,7 +4015,7 @@
"description": "$1 is the action type. e.g (Account, Transaction, Swap)"
},
"visitWebSite": {
"message": "Visite o nosso site"
"message": "Visite nosso site"
},
"walletConnectionGuide": {
"message": "nosso guia de conexão com a carteira de hardware"
@ -3987,6 +4040,9 @@
"walletCreationSuccessTitle": {
"message": "Carteira criada com sucesso"
},
"wantToAddThisNetwork": {
"message": "Desejar adicionar esta rede?"
},
"warning": {
"message": "Atenção"
},
@ -4049,6 +4105,10 @@
"yesLetsTry": {
"message": "Sim, vamos tentar"
},
"youHaveAddedAll": {
"message": "Você adicionou todas as redes populares. Você pode descobrir mais redes $1 Ou você pode $2",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "Você precisa permitir o acesso à câmera para usar esse recurso."
},

@ -1031,9 +1031,6 @@
"failureMessage": {
"message": "Ocorreu algum erro e não conseguimos concluir a ação"
},
"fakeTokenWarning": {
"message": "Qualquer um pode criar um token, incluindo versões falsas de tokens existentes. Saiba mais sobre $1"
},
"fast": {
"message": "Rápido"
},
@ -1517,7 +1514,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "Use $1 para aguardar um preço mais baixo. As estimativas de tempo são muito menos exatas, pois os preços são relativamente imprevisíveis.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "baixa"
@ -1549,7 +1546,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "Use $1 para um processamento rápido pelo preço atual de mercado.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "nota"
@ -1895,7 +1892,7 @@
},
"notifications8ActionText": {
"message": "Ir para Configurações Avançadas",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "A partir da MetaMask v10.4.0, não é mais necessário o Ledger Live para conectar o seu dispositivo Ledger à MetaMask.",
@ -2069,7 +2066,7 @@
},
"preferredLedgerConnectionType": {
"message": "Tipo de conexão preferencial com o Ledger",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"prev": {
"message": "Anterior"
@ -2991,7 +2988,7 @@
},
"toggleTestNetworks": {
"message": "$1 redes de teste",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "Token"
@ -3005,9 +3002,6 @@
"tokenDecimalFetchFailed": {
"message": "A casa decimal do token é necessária."
},
"tokenDetectionAnnouncement": {
"message": "Novidade! A detecção aprimorada de token está disponível na Mainnet do Ethereum como uma funcionalidade experimental. $1"
},
"tokenId": {
"message": "ID do token"
},
@ -3220,12 +3214,6 @@
"usePhishingDetectionDescription": {
"message": "Exibir uma advertência para os domínios de phishing destinados a usuários do Ethereum"
},
"useTokenDetection": {
"message": "Usar detecção de tokens"
},
"useTokenDetectionDescription": {
"message": "Utilizamos APIs terceirizadas para detectar e exibir novos tokens enviados à sua carteira. Desative essa opção se não deseja que a MetaMask extraia dados desses serviços."
},
"usedByClients": {
"message": "Usado por diversos clientes diferentes"
},

@ -157,6 +157,9 @@
"addMemo": {
"message": "Добавить примечание"
},
"addMoreNetworks": {
"message": "добавить другие сети вручную"
},
"addNetwork": {
"message": "Добавить сеть"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "Предупреждения"
},
"allOfYour": {
"message": "Все ваши $1",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "Разрешить этому внешнему расширению:"
},
@ -263,6 +270,10 @@
"approve": {
"message": "Одобрить лимит расходов"
},
"approveAllTokensTitle": {
"message": "Разрешить доступ к всем вашим $1?",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "Одобрить и установить"
},
@ -282,9 +293,6 @@
"approvedAsset": {
"message": "Одобренный актив"
},
"areYouDeveloper": {
"message": "Вы разработчик?"
},
"areYouSure": {
"message": "Вы уверены?"
},
@ -435,10 +443,10 @@
"description": "$1 represents the crypto symbol to be purchased"
},
"buyWithWyre": {
"message": "Купить $1 с помощью Wyre"
"message": "Купить 1 $ с помощью Wyre"
},
"buyWithWyreDescription": {
"message": "Wyre позволяет использовать дебетовую карту для внесения $1 прямо на ваш счет MetaMask."
"message": "Простая регистрация для покупок на сумму до 1000 $. Быстрая интерактивная проверка покупки с высоким лимитом. Поддерживает дебетовые/кредитные карты, Apple Pay, банковские переводы. Доступно в более чем 100 странах. Токены зачисляются на ваш счет MetaMask"
},
"bytes": {
"message": "Байты"
@ -466,6 +474,13 @@
"message": "Чтобы $1 транзакции плата за газ должна быть увеличена как минимум на 10%. Это позволит обеспечить прием транзакции сетью.",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "Отменить обмен на ~$1",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "Отменить обмен бесплатно"
},
"cancellationGasFee": {
"message": "Плата за газ при отмене"
},
@ -732,7 +747,7 @@
},
"customGasSettingToolTipMessage": {
"message": "Использовать $1, чтобы настроить цену на газ. Это может сбивать с толку, если вы не знакомы с этим. Взаимодействуйте на свой страх и риск.",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "Увеличение комиссии может сократить время обработки, но это не гарантируется."
@ -818,7 +833,7 @@
},
"depositCrypto": {
"message": "Внесите $1",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "Описание"
@ -1196,9 +1211,6 @@
"failureMessage": {
"message": "Что-то пошло не так, и мы не смогли завершить действие"
},
"fakeTokenWarning": {
"message": "Кто угодно может создать токен, в том числе создать поддельные версии существующих токенов. Узнайте подробнее о $1"
},
"fast": {
"message": "Быстрый"
},
@ -1277,6 +1289,9 @@
"functionApprove": {
"message": "Функция: Одобрить"
},
"functionSetApprovalForAll": {
"message": "Функция: SetApprovalForAll"
},
"functionType": {
"message": "Тип функции"
},
@ -1463,7 +1478,7 @@
},
"highGasSettingToolTipMessage": {
"message": "Используйте $1, чтобы компенсировать скачки сетевого трафика из-за таких событий, как дропы популярных NFT.",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "высокая"
@ -1617,6 +1632,9 @@
"invalidSeedPhrase": {
"message": "Неверная секретная фраза для восстановления"
},
"invalidSeedPhraseCaseSensitive": {
"message": "Неправильный ввод! Секретная фраза для восстановления чувствительна к регистру."
},
"ipfsGateway": {
"message": "Шлюз IPFS"
},
@ -1766,7 +1784,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "Используйте $1, чтобы дождаться более низкой цены. Оценки времени намного менее точны, поскольку цены в некоторой степени непредсказуемы.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "низкая"
@ -1810,7 +1828,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "Используйте $1 для быстрой обработки по текущей рыночной цене.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "заметка"
@ -1892,6 +1910,19 @@
"message": "проверить сведения о сети",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"mismatchedChainRecommendation": {
"message": "Мы рекомендуем вам $1, прежде чем продолжить.",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "Согласно нашим данным, имя сети может не соответствовать этому идентификатору блокчейна."
},
"mismatchedNetworkSymbol": {
"message": "Представленный символ валюты не соответствует тому, что мы ожидаем для этого идентификатора блокчейна."
},
"mismatchedRpcUrl": {
"message": "Согласно нашим записям, отправленное значение URL-адреса RPC не соответствует известному поставщику для этого идентификатора блокчейна."
},
"missingNFT": {
"message": "Не видите свои NFT?"
},
@ -1943,6 +1974,9 @@
"network": {
"message": "Сеть:"
},
"networkAddedSuccessfully": {
"message": "Сеть успешно добавлена!"
},
"networkDetails": {
"message": "Сведения о сети"
},
@ -2059,6 +2093,9 @@
"message": "Одноразовый номер больше, чем предложенный одноразовый номер $1",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "NFT"
},
"nftTokenIdPlaceholder": {
"message": "Введите ид. коллекционного актива"
},
@ -2130,7 +2167,7 @@
},
"notifications10ActionText": {
"message": "Смотреть в настройках",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "Улучшенное обнаружение токенов в настоящее время доступно в сетях Ethereum Mainnet, Polygon, BSC и Avalanche. Это еще не все!"
@ -2159,6 +2196,16 @@
"notifications12Title": {
"message": "Когда появится темный режим? Он уже появился! "
},
"notifications13ActionText": {
"message": "Показать пользовательский список сетей"
},
"notifications13Description": {
"message": "Теперь вы можете легко добавить следующие популярные пользовательские сети: Arbitrum, Avalanche, Binance Smart Chain, Fantom, Harmony, Optimism, Palm и Polygon! Чтобы включить эту функцию, перейдите в «Настройки» -> «Экспериментальная версия» и включите «Показать пользовательский список сетей»!",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "Добавить популярные сети"
},
"notifications1Description": {
"message": "Теперь пользователи MetaMask Mobile могут обменивать токены в своем мобильном кошельке. Отсканируйте QR-код, чтобы скачать мобильное приложение и начать обмен.",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2225,7 +2272,7 @@
},
"notifications8ActionText": {
"message": "Перейти в Дополнительные настройки",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "Начиная с версии MetaMask 10.4.0, вам больше не требуется Ledger Live для подключения леджера к MetaMask.",
@ -2261,10 +2308,6 @@
"notificationsMarkAllAsRead": {
"message": "Отметить все как прочитанные"
},
"numberOfNewTokensDetected": {
"message": "$1 новых токена(-ов) найдены в этом аккаунте",
"description": "$1 is the number of new tokens detected"
},
"ofTextNofM": {
"message": "из"
},
@ -2344,9 +2387,6 @@
"message": "Откройте MetaMask в полноэкранном режиме, чтобы подключить свой леджер через WebHID.",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "Проверьте исходный код"
},
"optional": {
"message": "Необязательно"
},
@ -2473,7 +2513,7 @@
},
"preferredLedgerConnectionType": {
"message": "Предпочтительный тип подключения к леджеру",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "Подготовка обмена..."
@ -2676,6 +2716,14 @@
"revealTheSeedPhrase": {
"message": "Показать сид-фразу"
},
"revokeAllTokensTitle": {
"message": "Отозвать разрешение на доступ ко всем вашим $1?",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "При отмене разрешения следующий $1 больше не сможет получить доступ к вашему $2",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Тестовая сеть Rinkeby"
},
@ -2852,12 +2900,23 @@
"message": "Отправка $1...",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "Предупреждение: вы собираетесь отправить токен-контракт, что может привести к потере средств. $1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "Задать дополнительные настройки конфиденциальности"
},
"setAdvancedPrivacySettingsDetails": {
"message": "MetaMask использует эти доверенные сторонние сервисы для повышения удобства использования и безопасности продукта."
},
"setApprovalForAll": {
"message": "Установить одобрение для всех"
},
"setApprovalForAllTitle": {
"message": "Одобрить $1 без ограничений по расходам",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "Настройки"
},
@ -2877,6 +2936,12 @@
"showAdvancedGasInlineDescription": {
"message": "Выберите это, чтобы отображать цену газа и управление лимитами непосредственно на экранах отправки и подтверждения."
},
"showCustomNetworkList": {
"message": "Показать пользовательский список сетей"
},
"showCustomNetworkListDescription": {
"message": "Выберите это, чтобы отобразить список сетей с предварительно заполненными данными при добавлении новой сети."
},
"showFiatConversionInTestnets": {
"message": "Показывать конвертацию в тестовых сетях"
},
@ -2967,10 +3032,6 @@
"snapInstallWarningCheck": {
"message": "Чтобы подтвердить, что вы понимаете, отметьте все."
},
"snapInstallWarningKeyAccess": {
"message": "Вы предоставляете ключевой доступ к снапу «$1». Это действие является безотзывным и предоставляет «$1» контроль над вашими счетами и активами. Прежде чем продолжить, убедитесь, что вы доверяете «$1».",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "Этот снап запрашивает следующие разрешения:"
},
@ -2986,6 +3047,9 @@
"snapsToggle": {
"message": "Снап будет работать только в том случае, если он включен"
},
"someNetworksMayPoseSecurity": {
"message": "Некоторые сети могут представлять угрозу безопасности и/или конфиденциальности. Прежде чем добавлять и использовать сеть, ознакомьтесь с рисками."
},
"somethingWentWrong": {
"message": "Ой! Что-то пошло не так."
},
@ -3548,6 +3612,10 @@
"switchNetworks": {
"message": "Сменить сети"
},
"switchToNetwork": {
"message": "Переключиться на $1",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "Переключиться на этот счет"
},
@ -3635,7 +3703,7 @@
},
"toggleTestNetworks": {
"message": "$1 тестовые сети",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "Токен"
@ -3658,18 +3726,6 @@
"tokenDetails": {
"message": "Сведения о токене"
},
"tokenDetection": {
"message": "Обнаружение токена"
},
"tokenDetectionAlertMessage": {
"message": "Обнаружение токена в настоящее время доступно на $1. $2"
},
"tokenDetectionAnnouncement": {
"message": "Новинка! Улучшенное обнаружение токенов доступно в сети Ethereum Mainnet в качестве экспериментальной функции. $1"
},
"tokenDetectionToggleDescription": {
"message": "API токенов ConsenSys объединяет список токенов из различных списков сторонних токенов. Отключение этого параметра прекратит обнаружение новых токенов, добавляемых в ваш кошелек, но сохранит возможность поиска токенов для импорта."
},
"tokenId": {
"message": "Ид. токена"
},
@ -3851,6 +3907,9 @@
"unknownCameraErrorTitle": {
"message": "Ой! Что-то пошло не так...."
},
"unknownCollection": {
"message": "Безымянная коллекция"
},
"unknownNetwork": {
"message": "Неизвестная частная сеть"
},
@ -3901,12 +3960,6 @@
"usePhishingDetectionDescription": {
"message": "Показывать предупреждение для фишинговых доменов, нацеленных на пользователей Ethereum"
},
"useTokenDetection": {
"message": "Использовать обнаружение токенов"
},
"useTokenDetectionDescription": {
"message": "Мы используем сторонние API для обнаружения и отображения новых токенов, отправленных в ваш кошелек. Отключите, если не хотите, чтобы MetaMask получал данные от этих служб."
},
"useTokenDetectionPrivacyDesc": {
"message": "Автоматическое отображение токенов, отправленных на ваш счет, требует обмена данными со сторонними серверами для получения изображений токенов. Эти серверы получат доступ к вашему IP-адресу."
},
@ -3987,6 +4040,9 @@
"walletCreationSuccessTitle": {
"message": "Кошелек создан"
},
"wantToAddThisNetwork": {
"message": "Хотите добавить эту сеть?"
},
"warning": {
"message": "Предупреждение"
},
@ -4049,6 +4105,10 @@
"yesLetsTry": {
"message": "Да, давайте попробуем"
},
"youHaveAddedAll": {
"message": "Вы добавили все популярные сети. Вы можете открыть для себя больше сетей $1 или можете $2",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "Для использования этой функции вам необходимо предоставить доступ к камере."
},

@ -157,6 +157,9 @@
"addMemo": {
"message": "Magdagdag ng memo"
},
"addMoreNetworks": {
"message": "magdagdag pa ng mga network nang mano-mano"
},
"addNetwork": {
"message": "Magdagdag ng Network"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "Mga Alerto"
},
"allOfYour": {
"message": "Lahat ng iyong $1",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "Payagan ang external extension na ito na:"
},
@ -263,6 +270,10 @@
"approve": {
"message": "Aprubahan ang limitasyon sa paggastos"
},
"approveAllTokensTitle": {
"message": "Magbigay ng pahintulot na i-access ang lahat ng iyong $1?",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "Aprubahan at I-install"
},
@ -282,9 +293,6 @@
"approvedAsset": {
"message": "Aprubadong asset"
},
"areYouDeveloper": {
"message": "Isa ka bang developer?"
},
"areYouSure": {
"message": "Sigurado ka ba?"
},
@ -466,6 +474,13 @@
"message": "Sa $1 na transaksyon ang singil sa gas ay dapat tumaas nang hindi bababa sa 10% para ito ay makilala ng network.",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "Kanselahin ang swap sa halagang ~$1",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "Kanselahin ang swap nang libre"
},
"cancellationGasFee": {
"message": "Bayarin sa Gasolina para sa Pagkansela"
},
@ -732,7 +747,7 @@
},
"customGasSettingToolTipMessage": {
"message": "Gamitin ang $1 para i-customize ang presyo ng gas. Ito ay maaaring nakakalito kung hindi ka pamilyar. Harapin ang sarili mong panganib.",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "Kapag dinagdagan ang bayarin, mababawasan ang mga oras ng pagproseso, pero hindi ito garantisado."
@ -818,7 +833,7 @@
},
"depositCrypto": {
"message": "Magdeposito ng $1",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "Deskripsyon"
@ -1196,9 +1211,6 @@
"failureMessage": {
"message": "Nagkaproblema, at hindi namin makumpleto ang aksyon"
},
"fakeTokenWarning": {
"message": "Sinuman ay maaaring gumawa ng token, kabilang ang paggawa ng mga pekeng bersyon ng mga umiiral na token. Alamin pa ang tungkol sa $1"
},
"fast": {
"message": "Mabilis"
},
@ -1277,6 +1289,9 @@
"functionApprove": {
"message": "Function: Aprubahan"
},
"functionSetApprovalForAll": {
"message": "Function: ItakdaAngPag-aprubaParaSaLahat"
},
"functionType": {
"message": "Uri ng Function"
},
@ -1463,7 +1478,7 @@
},
"highGasSettingToolTipMessage": {
"message": "Gamitin ang $1 upang pagtakpan ang mga surge sa network traffic dahil sa mga bagay tulad ng popular na pagbagsak ng NFT.",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "mataas"
@ -1617,6 +1632,9 @@
"invalidSeedPhrase": {
"message": "Hindi valid ang Secret Recovery Phrase"
},
"invalidSeedPhraseCaseSensitive": {
"message": "Di-wastong input! Ang Secret Recovery Phrase ay case sensitive."
},
"ipfsGateway": {
"message": "Gateway na IPFS"
},
@ -1766,7 +1784,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "Gamitin ang $1 para maghintay ng mas murang presyo. Ang mga pagtatantya sa oras ay hindi gaanong tumpak dahil ang mga presyo ay medyo hindi mahuhulaan.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "mababa"
@ -1810,7 +1828,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "Gamitin ang $1 para sa pagproseso sa kasalukuyang market price.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "memo"
@ -1892,6 +1910,19 @@
"message": "i-verify ang mga detalye ng network",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"mismatchedChainRecommendation": {
"message": "Inirerekomenda namin na $1 ka bago magpatuloy.",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "Ayon sa aming talaan, ang pangalan ng network ay maaaring hindi tumugma nang tama sa chain ID na ito."
},
"mismatchedNetworkSymbol": {
"message": "Ang isinumiteng simbolo ng currency ay hindi tumutugma sa inaasahan namin para sa chain ID na ito."
},
"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?"
},
@ -1943,6 +1974,9 @@
"network": {
"message": "Network:"
},
"networkAddedSuccessfully": {
"message": "Matagumpay na naidagdag ang network!"
},
"networkDetails": {
"message": "Mga Detalye ng Network"
},
@ -2059,6 +2093,9 @@
"message": "Mas mataas ang noncesa iminumungkahing nonce na $1",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "NFT"
},
"nftTokenIdPlaceholder": {
"message": "Ilagay ang collectible ID"
},
@ -2130,7 +2167,7 @@
},
"notifications10ActionText": {
"message": "Bisitahin sa mga setting",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "Ang pinahusay na pagtukoy ng token ay kasalukuyang magagamit sa Ethereum Mainnet, Polygon, BSC, at mga Avalanche network. Marami pang darating!"
@ -2159,6 +2196,16 @@
"notifications12Title": {
"message": "Wen dark mode? Ngayon dark mode! 🕶🦊"
},
"notifications13ActionText": {
"message": "Ipakita ang listahan ng custom na network"
},
"notifications13Description": {
"message": "Madali mo na ngayong maidagdag ang mga sumusunod na sikat na custom na network: Arbitrum, Avalanche, Binance Smart Chain, Fantom, Harmony, Optimism, Palm at Polygon! Para i-enable ang feature na ito, pumunta sa Mga Setting -> Experimental at i-on ang \"Ipakita ang listahan ng custom na network\"!",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "Magdagdag ng mga Sikat na Network"
},
"notifications1Description": {
"message": "Ang mga user ng MetaMask Mobile ay maaari na ngayong mag-swap ng mga token sa loob ng kanilang mobile wallet. I-scan ang QR code para makuha ang mobile app at magsimulang mag-swap.",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2225,7 +2272,7 @@
},
"notifications8ActionText": {
"message": "Magpunta sa Advanced Settings",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "Para sa MetaMask v10.4.0, hindi mo na kailangang ikonekta ang Ledger Live sa iyong Ledger device sa MetaMask.",
@ -2261,10 +2308,6 @@
"notificationsMarkAllAsRead": {
"message": "Markahan ang lahat bilang nabasa na"
},
"numberOfNewTokensDetected": {
"message": "$1 bagong token ang nakita sa account na ito",
"description": "$1 is the number of new tokens detected"
},
"ofTextNofM": {
"message": "ng"
},
@ -2344,9 +2387,6 @@
"message": "Buksan ang MetaMask sa buong screen para ikonekta ang ledger mo sa pamamagitan ng WebHID.",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "Suriin ang code ng pinagmulan"
},
"optional": {
"message": "Opsyonal"
},
@ -2473,7 +2513,7 @@
},
"preferredLedgerConnectionType": {
"message": "Napiling Uri ng Ledger Connection",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "Inihahanda ang pagpapalit..."
@ -2676,6 +2716,14 @@
"revealTheSeedPhrase": {
"message": "Ipakita ang seed phrase"
},
"revokeAllTokensTitle": {
"message": "Bawiin ang pahintulot na i-access ang lahat ng iyong $1?",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "Sa pamamagitan ng pagbawi ng pahintulot, hindi na maa-access ng sumusunod na $1 ang iyong $2",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Rinkeby Test Network"
},
@ -2852,12 +2900,23 @@
"message": "Nagpapadala ng $1",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "Babala: magpapadala ka sa isang kontrata ng token na maaaring magresulta sa pagkawala ng mga pondo. $1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "Magtakda ng advanced privacy settings"
},
"setAdvancedPrivacySettingsDetails": {
"message": "Ginagamit ng MetaMask ang mga pinagkakatiwalaang serbisyo ng third-party na ito para mapahusay ang kakayahang magamit at kaligtasan ng produkto."
},
"setApprovalForAll": {
"message": "Itakda ang Pag-apruba para sa Lahat"
},
"setApprovalForAllTitle": {
"message": "Aprubahan ang $1 nang walang limitasyon sa paggastos",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "Mga Setting"
},
@ -2877,6 +2936,12 @@
"showAdvancedGasInlineDescription": {
"message": "Piliin ito para direktang maipakita ang presyo ng gas at mga kontrol sa limitasyon sa mga screen ng pagpapadala at pagkumpirma."
},
"showCustomNetworkList": {
"message": "Ipakita ang Listahan ng Custom na Network"
},
"showCustomNetworkListDescription": {
"message": "Piliin ito para magpakita ng listahan ng mga network na may prefilled na mga detalye kapag nagdaragdag ng bagong network."
},
"showFiatConversionInTestnets": {
"message": "Ipakita ang Conversion sa Testnets"
},
@ -2967,10 +3032,6 @@
"snapInstallWarningCheck": {
"message": "Para kumpirmahing naunawaan mo, tsekan lahat."
},
"snapInstallWarningKeyAccess": {
"message": "Ipinagkakaloob mo ang key access sa snap \"$1\". Hindi ito maaaring bawiin at ipinagkakaloob sa \"$1\" ang kontrol sa iyong mga account at mga asset. Tiyaking pinagkakatiwalaan mo ang \"$1\" bago magpatuloy.",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "Hinihiling ng snap na ito ang mga sumusunod na pahintulot:"
},
@ -2986,6 +3047,9 @@
"snapsToggle": {
"message": "Tatakbo lamang ang snap kapag pinagana ito"
},
"someNetworksMayPoseSecurity": {
"message": "Maaaring magdulot ang ilang network ng mga panganib sa seguridad at/o pagkapribado. Unawain ang mga panganib bago idagdag o gamitin ang isang network."
},
"somethingWentWrong": {
"message": "Oops! Nagkaproblema."
},
@ -3548,6 +3612,10 @@
"switchNetworks": {
"message": "Lumipat ng Network"
},
"switchToNetwork": {
"message": "Lumipat sa $1",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "Lumipat sa account na ito"
},
@ -3635,7 +3703,7 @@
},
"toggleTestNetworks": {
"message": "$1 na test network",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "Token"
@ -3658,18 +3726,6 @@
"tokenDetails": {
"message": "Mga detalye ng token"
},
"tokenDetection": {
"message": "Pagtuklas sa token"
},
"tokenDetectionAlertMessage": {
"message": "Ang pagtukoy ng token ay kasalukuyang magagamit sa $1. $2"
},
"tokenDetectionAnnouncement": {
"message": "Bago! Ang pinahusay na pagtukoy ng token ay magagamit sa Ethereum Mainnet bilang isang pang-eksperimentong feature. $1"
},
"tokenDetectionToggleDescription": {
"message": "Pinagsasama-sama ng ConsenSys’ token API ang listahan ng mga token mula sa maraming listahan ng token ng third party. Ang pag-off dito ay magpapahinto sa pagtuklas ng mga bagong token na madadagdag sa iyong wallet, ngunit pananatilihin ang opsyon sa paghahanap ng mga token para i-import."
},
"tokenId": {
"message": "Token ID"
},
@ -3851,6 +3907,9 @@
"unknownCameraErrorTitle": {
"message": "Ooops! Nagkaproblema...."
},
"unknownCollection": {
"message": "Walang pangalang koleksyon"
},
"unknownNetwork": {
"message": "Hindi Alam na Pribadong Network"
},
@ -3901,12 +3960,6 @@
"usePhishingDetectionDescription": {
"message": "Magpakita ng babala para sa mga phishing domain na nagta-target sa mga user ng Ethereum"
},
"useTokenDetection": {
"message": "Gamitin ang Pag-detect ng Token"
},
"useTokenDetectionDescription": {
"message": "Gumagamit kami ng mga third-party na API para makita at magpakita ng mga bagong token na ipinadala sa iyong wallet. I-off kung ayaw mong makuha ng MetaMask ang data mula sa mga serbisyong iyon."
},
"useTokenDetectionPrivacyDesc": {
"message": "Awtomatikong ipinapakita ang mga token na ipinadala sa iyong account na nakapaloob sa komunikasyon ng mga server ng third party para makuha ang mga larawan ng token. Ang mga server na iyon ay magkakaroon ng access sa iyong IP address."
},
@ -3987,6 +4040,9 @@
"walletCreationSuccessTitle": {
"message": "Matagumpay ang paggawa ng wallet"
},
"wantToAddThisNetwork": {
"message": "Gusto mo bang idagdag ang network na ito?"
},
"warning": {
"message": "Babala"
},
@ -4049,6 +4105,10 @@
"yesLetsTry": {
"message": "Oo, subukan natin"
},
"youHaveAddedAll": {
"message": "Idinagdag mo ang lahat ng sikat na network. Maaari kang makatuklas ng higit pang mga network $1 O maaari kang",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "Kailangan mong payagan ang pag-access sa camera para magamit ang feature na ito."
},

@ -157,6 +157,9 @@
"addMemo": {
"message": "Not ekleyin"
},
"addMoreNetworks": {
"message": "manuel olarak daha fazla ağ ekleyin"
},
"addNetwork": {
"message": "Ağ ekle"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "Uyarılar"
},
"allOfYour": {
"message": "Sahip olduğunuz tüm $1",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "Bu harici uzantının şunu yapmasına izin ver:"
},
@ -263,6 +270,10 @@
"approve": {
"message": "Harcama limitini onayla"
},
"approveAllTokensTitle": {
"message": "Sahip olduğunuz tüm $1 için erişim izni verilsin mi?",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "Onayla ve Yükle"
},
@ -282,9 +293,6 @@
"approvedAsset": {
"message": "Onaylanan varlık"
},
"areYouDeveloper": {
"message": "Geliştirici misin?"
},
"areYouSure": {
"message": "Emin misin?"
},
@ -438,7 +446,7 @@
"message": "Wyre ile $1 satın al"
},
"buyWithWyreDescription": {
"message": "Wyre, doğrudan MetaMask hesabınıza $1 yatırma işlemleri için banka kartı kullanmanıza izin verir."
"message": "1000$'a kadar satın alma işlemlerinde kolay oryantasyon. Banka Kartı/Kredi Kartı, Apple Pay, Banka Transferlerini destekler. +100 ülkede kullanılabilir. Token'lar MetaMask Hesabına yatırılır"
},
"bytes": {
"message": "Bayt"
@ -466,6 +474,13 @@
"message": "İşlemi $1 için, gaz ücretinin ağ tarafından tanınması amacıyla en az %10 oranında artırılması gerekir.",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "~$1 için swap işlemini iptal edin",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "Swap işlemini ücretsiz iptal edin"
},
"cancellationGasFee": {
"message": "İptal İşlemi Gaz Ücreti"
},
@ -732,7 +747,7 @@
},
"customGasSettingToolTipMessage": {
"message": "Gaz fiyatını özelleştirmek için $1 kullanın. Bu, bilgi sahibi değilseniz kafa karıştırıcı olabilir. Riski size ait olmak üzere kullanın.",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "Ücretin artırılması işlem süresini kısaltabilir ancak bu garanti edilmez."
@ -818,7 +833,7 @@
},
"depositCrypto": {
"message": "$1 yatır",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "Açıklama"
@ -1196,9 +1211,6 @@
"failureMessage": {
"message": "Bir şeyler ters gitti ve işlemi tamamlayamadık"
},
"fakeTokenWarning": {
"message": "Mevcut tokenlerin sahteleri de dahil olmak üzere herkes bir token oluşturabilir. $1 hakkında daha fazla bilgi edinin"
},
"fast": {
"message": "Hızlı"
},
@ -1277,6 +1289,9 @@
"functionApprove": {
"message": "İşlev: Onayla"
},
"functionSetApprovalForAll": {
"message": "İşlev: TümüİçinOnayVer"
},
"functionType": {
"message": "İşlev Türü"
},
@ -1463,7 +1478,7 @@
},
"highGasSettingToolTipMessage": {
"message": "Popüler NFT düşüşleri gibi şeyler nedeniyle ağ trafiğindeki dalgalanmaları kapsayacak şekilde $1 kullanın.",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "yüksek"
@ -1617,6 +1632,9 @@
"invalidSeedPhrase": {
"message": "Gizli Kurtarma İfadesi geçersiz"
},
"invalidSeedPhraseCaseSensitive": {
"message": "Giriş geçersiz! Gizli Kurtarma İfadesi büyük/küçük harf duyarlıdır."
},
"ipfsGateway": {
"message": "IPFS Ağ Geçidi"
},
@ -1766,7 +1784,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "Daha ucuz bir fiyat için beklemek için $1 kullanın. Fiyatlar bir şekilde öngörülemez oldukları için süre tahminleri çok daha az kesindir.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "düşük"
@ -1810,7 +1828,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "Mevcut piyasa fiyatında hızlı işleme almak için $1 kullanın.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "not"
@ -1892,6 +1910,19 @@
"message": "ağ bilgilerini doğrula",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"mismatchedChainRecommendation": {
"message": "İlerlemeden önce şunu öneririz: $1.",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "Kayıtlarımıza göre, ağ adı bu zincir kimliği ile doğru bir şekilde uyumlu olmayabilir."
},
"mismatchedNetworkSymbol": {
"message": "Sunulan para birimi sembolü bu zincir kimliği için beklediğimiz sembolle uyumlu değil."
},
"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?"
},
@ -1943,6 +1974,9 @@
"network": {
"message": "Ağ:"
},
"networkAddedSuccessfully": {
"message": "Ağ başarılı bir şekilde eklendi!"
},
"networkDetails": {
"message": "Ağ Bilgileri"
},
@ -2059,6 +2093,9 @@
"message": "Geçici anahtar, önerilen $1 geçici anahtarından daha büyük",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "NFT"
},
"nftTokenIdPlaceholder": {
"message": "Toplanabilir kimliğini girin"
},
@ -2130,7 +2167,7 @@
},
"notifications10ActionText": {
"message": "Ayarlarda ziyaret et",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "Geliştirilmiş token algılama şu anda Ethereum Mainnet, Polygon, BSC ve Avalanche ağlarında mevcut. Daha fazlası gelecek!"
@ -2154,11 +2191,21 @@
"message": "Karanlık modu etkinleştir"
},
"notifications12Description": {
"message": "Karanlık Mod, sistem tercihlerine bağlı olarak yeni kullanıcılar için etkinleştirilecektir. Mevcut kullanıcılar için, Ayarlar -> Deneysel altında Karanlık Modu manuel olarak etkinleştir."
"message": "Uzantıda karanlık mod sonunda burada! Bunu açmak için Ayarlar -> Deneysel kısmına git ve ekran seçeneklerinden birini seç: Aydınlık, Karanlık, Sistem."
},
"notifications12Title": {
"message": "Karanlık mod mu? Şimdi karanlık mod! 🕶🦊"
},
"notifications13ActionText": {
"message": "Özel ağ listesini göster"
},
"notifications13Description": {
"message": "Artık şu popüler özel ağları kolayca ekleyebilirsiniz: Arbitrum, Avalanche, Binance Smart Chain, Fantom, Harmony, Optimism, Palm ve Polygon! Bu özelliği etkinleştirmek için Ayarlar -> Deneysel kısmına gidip \"Özel ağ listesini göster\" seçeneğini açın!",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "Popüler Ağları Ekle"
},
"notifications1Description": {
"message": "MetaMask Mobil kullanıcıları artık mobil cüzdanları içinde token takas edebilirler. Mobil uygulamayı edinmek ve takas yapmaya başlamak için QR kodunu tarayın.",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2225,7 +2272,7 @@
},
"notifications8ActionText": {
"message": "Gelişmiş ayarlara git",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "MetaMask 10.4.0 sürümü itibariyle Kayıt Defteri cihazınızı Metamask'e bağlamak için artık Ledger Live'e ihtiyacınız yok.",
@ -2261,10 +2308,6 @@
"notificationsMarkAllAsRead": {
"message": "Tümünü okundu olarak işaretle"
},
"numberOfNewTokensDetected": {
"message": "Bu hesapta $1 yeni token bulundu",
"description": "$1 is the number of new tokens detected"
},
"ofTextNofM": {
"message": "/"
},
@ -2344,9 +2387,6 @@
"message": "Kayıt defterinizi WebHID üzerinden bağlamak için MetaMask'i tam ekran açın.",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "Kaynak kodunu kontrol et"
},
"optional": {
"message": "İsteğe bağlı"
},
@ -2473,7 +2513,7 @@
},
"preferredLedgerConnectionType": {
"message": "Tercih Edilen Kayıt Defteri Bağlantı Türü",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "Takas hazırlanıyor..."
@ -2676,6 +2716,14 @@
"revealTheSeedPhrase": {
"message": "Tohum ifadesini ortaya çıkar"
},
"revokeAllTokensTitle": {
"message": "Sahip olduğunuz tüm $1 için izin geri çekilsin mi?",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "İzni geri çektiğinizde aşağıdaki $1 artık $2 alanınıza erişim sağlayamayacak",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Rinkeby Test Ağı"
},
@ -2852,12 +2900,23 @@
"message": "$1 Gönderiliyor",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "Uyarı: Para kaybı ile sonuçlanabilecek bir token sözleşmesi göndermek üzeresiniz. $1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "Gelişmiş gizlilik ayarlarını yapın"
},
"setAdvancedPrivacySettingsDetails": {
"message": "MetaMask, ürünün kullanılabilirliğini ve güvenliğini iyileştirmek amacıyla bu güvenilir üçüncü taraf hizmetlerini kullanır."
},
"setApprovalForAll": {
"message": "Tümüne Onay Ver"
},
"setApprovalForAllTitle": {
"message": "$1 için harcama limiti olmadan onay ver",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "Ayarlar"
},
@ -2877,6 +2936,12 @@
"showAdvancedGasInlineDescription": {
"message": "Gaz fiyatı ve limit kontrollerini doğrudan gönder ve onayla ekranlarında göstermek için bunu seçin."
},
"showCustomNetworkList": {
"message": "Özel Ağ Listesini Göster"
},
"showCustomNetworkListDescription": {
"message": "Yeni bir ağ eklerken önceden doldurulan bilgilerle ağ listesini görüntülenmesi için bunu seçin."
},
"showFiatConversionInTestnets": {
"message": "Test ağlarında Dönüşümü göster"
},
@ -2967,10 +3032,6 @@
"snapInstallWarningCheck": {
"message": "Anladığını doğrulamak için hepsini kontrol et."
},
"snapInstallWarningKeyAccess": {
"message": "\"$1\" snap'ine anahtar erişimi veriyorsun. Bu geri alınamaz ve hesapların ve varlıkların üzerinde \"1$\" kontrol sağlar. Devam etmeden önce \"$1\" öğesine güvendiğinden emin ol.",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "Bu ek, aşağıdaki izinleri istiyor:"
},
@ -2986,6 +3047,9 @@
"snapsToggle": {
"message": "Bir snap yalnızca etkinleştirilmişse çalışır"
},
"someNetworksMayPoseSecurity": {
"message": "Bazı ağlar güvenlik ve/veya gizlilik riskleri teşkil edebilir. Bir ağ eklemeden ve kullanmadan önce riskleri anlayın."
},
"somethingWentWrong": {
"message": "Eyvah! Bir şeyler ters gitti."
},
@ -3548,6 +3612,10 @@
"switchNetworks": {
"message": "Ağları Değiştir"
},
"switchToNetwork": {
"message": "$1 ağına geçin",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "Bu hesaba geç"
},
@ -3635,7 +3703,7 @@
},
"toggleTestNetworks": {
"message": "Test ağlarını $1",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "Token"
@ -3658,18 +3726,6 @@
"tokenDetails": {
"message": "Token bilgileri"
},
"tokenDetection": {
"message": "Token algılama"
},
"tokenDetectionAlertMessage": {
"message": "Token algılama şu anda $1 üzerinden kullanılabilir. $2"
},
"tokenDetectionAnnouncement": {
"message": "Yeni! Deney aşamasında olan bir özellik olarak Ethereum Mainnet'te gelişmiş token algılama mevcut. $1"
},
"tokenDetectionToggleDescription": {
"message": "ConsenSys'in token API'si, çeşitli üçüncü taraf token listelerinden token'ların bir listesini toplar. Kapatmak, cüzdanına eklenen yeni token'ları algılamayı durduracak, ancak içe aktarılacak token'ları arama seçeneğini koruyacaktır."
},
"tokenId": {
"message": "Token Kimliği"
},
@ -3851,6 +3907,9 @@
"unknownCameraErrorTitle": {
"message": "Eyvah! Bir şeyler ters gitti...."
},
"unknownCollection": {
"message": "İsimsiz koleksiyon"
},
"unknownNetwork": {
"message": "Bilinmeyen Özel Ağ"
},
@ -3901,12 +3960,6 @@
"usePhishingDetectionDescription": {
"message": "Ethereum kullanıcılarını hedefleyen kimlik avı alanları için bir uyarı görüntüler"
},
"useTokenDetection": {
"message": "Token Algılama Kullan"
},
"useTokenDetectionDescription": {
"message": "Cüzdanınıza gönderilen yeni tokenleri algılamak ve görüntülemek için üçüncü taraf API'leri kullanıyoruz. MetaMask tarafından bu hizmetlerden veri çekilmesini istemiyorsanız bunu kapatın."
},
"useTokenDetectionPrivacyDesc": {
"message": "Hesabına gönderilen token'ların otomatik olarak görüntülenmesi, token'ın görüntülerini almak için üçüncü taraf sunucularla iletişimi içerir. Bu servislerin IP adresine erişimi olacaktır."
},
@ -3962,7 +4015,7 @@
"description": "$1 is the action type. e.g (Account, Transaction, Swap)"
},
"visitWebSite": {
"message": "Web sitemizi ziyaret edin"
"message": "Web sitemizi ziyaret et"
},
"walletConnectionGuide": {
"message": "donanım cüzdanı bağlantı kılavuzumuz"
@ -3987,6 +4040,9 @@
"walletCreationSuccessTitle": {
"message": "Cüzdan oluşturma başarılı"
},
"wantToAddThisNetwork": {
"message": "Bu ağı eklemek istiyor musunuz?"
},
"warning": {
"message": "Uyarı"
},
@ -4049,6 +4105,10 @@
"yesLetsTry": {
"message": "Evet, deneyelim"
},
"youHaveAddedAll": {
"message": "Tüm popüler ağları eklediniz. $1 daha fazla ağ gekşefedebilir veya $2 seçeneğini seçebilirsiniz",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "Bu özelliği kullanmak için kamera erişimine izin vermeniz gerekir."
},

@ -157,6 +157,9 @@
"addMemo": {
"message": "Thêm bản ghi nhớ"
},
"addMoreNetworks": {
"message": "thêm thủ công các mạng khác"
},
"addNetwork": {
"message": "Thêm mạng"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "Cảnh báo"
},
"allOfYour": {
"message": "Tất cả $1 của bạn",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "Cho phép tiện ích bên ngoài này:"
},
@ -263,6 +270,10 @@
"approve": {
"message": "Phê duyệt giới hạn chi tiêu"
},
"approveAllTokensTitle": {
"message": "Cấp quyền truy cập vào tất cả $1 của bạn?",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "Chấp nhận và cài đặt"
},
@ -282,9 +293,6 @@
"approvedAsset": {
"message": "Tài sản được chấp nhận"
},
"areYouDeveloper": {
"message": "Bạn có phải là lập trình viên không?"
},
"areYouSure": {
"message": "Bạn có chắc chắn không?"
},
@ -438,7 +446,7 @@
"message": "Mua $1 qua Wyre"
},
"buyWithWyreDescription": {
"message": "Wyre cho phép bạn dùng thẻ ghi nợ để nạp $1 trực tiếp vào tài khoản MetaMask của mình."
"message": "Dễ dàng tham gia đối với các giao dịch mua lên đến $1.000. Xác minh mua hàng giới hạn cao và tương tác nhanh. Hỗ trợ Thẻ Tín dụng/Ghi nợ, Apple Pay, Chuyển khoản Ngân hàng. Hiện có tại hơn 100 quốc gia. Nạp token vào Tài khoản MetaMask của bạn"
},
"bytes": {
"message": "Byte"
@ -466,6 +474,13 @@
"message": "Để $1 một giao dịch, phí gas phải tăng tối thiểu 10% để mạng nhận ra giao dịch này.",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "Hủy hoán đổi với giá ~$1",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "Hủy hoán đổi miễn phí"
},
"cancellationGasFee": {
"message": "Phí gas hủy"
},
@ -650,7 +665,7 @@
"message": "Tương tác với hợp đồng"
},
"convertTokenToNFTDescription": {
"message": "Chúng tôi phát hiện tài sản này là một NFT. MetaMask hiện đã hỗ trợ gốc đầy đủ cho NFT. Bạn có muốn xóa tài sản khỏi danh sách token và thêm tài sản dưới dạng NFT không?"
"message": "Chúng tôi phát hiện tài sản này là một NFT. MetaMask hiện đã hỗ trợ toàn diện và đầy đủ cho NFT. Bạn có muốn xóa tài sản khỏi danh sách token và thêm tài sản dưới dạng NFT không?"
},
"convertTokenToNFTExistDescription": {
"message": "Chúng tôi phát hiện tài sản này đã được thêm dưới dạng NFT. Bạn có muốn xóa tài sản khỏi danh sách token không?"
@ -732,7 +747,7 @@
},
"customGasSettingToolTipMessage": {
"message": "Sử dụng $1 để tùy chỉnh giá gas. Việc này có thể gây nhầm lẫn nếu bạn không quen thuộc. Bạn phải tự chịu trách nhiệm nếu thực hiện.",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "Việc tăng phí có thể giúp giảm thời gian xử lý, nhưng điều này không được đảm bảo."
@ -818,7 +833,7 @@
},
"depositCrypto": {
"message": "Nạp $1",
"description": "$1 represents the cypto symbol to be purchased"
"description": "$1 represents the crypto symbol to be purchased"
},
"description": {
"message": "Mô tả"
@ -1196,9 +1211,6 @@
"failureMessage": {
"message": "Đã xảy ra sự cố và chúng tôi không thể hoàn tất hành động"
},
"fakeTokenWarning": {
"message": "Bất kỳ ai cũng có thể tạo token, bao gồm cả phiên bản giả mạo của các token hiện tại. Tìm hiểu thêm về $1"
},
"fast": {
"message": "Nhanh"
},
@ -1277,6 +1289,9 @@
"functionApprove": {
"message": "Chức năng: Phê duyệt"
},
"functionSetApprovalForAll": {
"message": "Chức năng: SetApprovalForAll"
},
"functionType": {
"message": "Loại chức năng"
},
@ -1463,7 +1478,7 @@
},
"highGasSettingToolTipMessage": {
"message": "Sử dụng $1 để bù đắp khi lưu lượng mạng lưới tăng vọt trong những trường hợp như phát hành NFT nổi tiếng.",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "cao"
@ -1617,6 +1632,9 @@
"invalidSeedPhrase": {
"message": "Cụm mật khẩu khôi phục bí mật không hợp lệ"
},
"invalidSeedPhraseCaseSensitive": {
"message": "Nội dung nhập không hợp lệ! Cụm từ khôi phục bí mật phân biệt chữ hoa và chữ thường."
},
"ipfsGateway": {
"message": "Cổng kết nối IPFS"
},
@ -1766,7 +1784,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "Sử dụng $1 để chờ mức giá rẻ hơn. Thời gian dự kiến sẽ kém chính xác hơn nhiều do mức giá tương đối khó dự đoán.",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "thấp"
@ -1810,7 +1828,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "Sử dụng $1 để xử lý nhanh theo giá thị trường hiện tại.",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "bản ghi nhớ"
@ -1892,6 +1910,19 @@
"message": "xác minh thông tin về mạng",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"mismatchedChainRecommendation": {
"message": "Bạn nên $1 trước khi tiếp tục.",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "Theo hồ sơ của chúng tôi, tên mạng có thể không khớp hoàn toàn với ID chuỗi này."
},
"mismatchedNetworkSymbol": {
"message": "Ký hiệu đơn vị tiền tệ đã gửi không khớp với những gì chúng tôi mong đợi cho ID chuỗi này."
},
"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?"
},
@ -1943,6 +1974,9 @@
"network": {
"message": "Mạng:"
},
"networkAddedSuccessfully": {
"message": "Đã thêm mạng thành công!"
},
"networkDetails": {
"message": "Thông tin về mạng"
},
@ -2059,6 +2093,9 @@
"message": "Số chỉ dùng một lần lớn hơn số chỉ dùng một lần gợi ý là $1",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "NFT"
},
"nftTokenIdPlaceholder": {
"message": "Nhập ID bộ sưu tập"
},
@ -2130,7 +2167,7 @@
},
"notifications10ActionText": {
"message": "Xem trong cài đặt",
"description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page."
"description": "The 'call to action' on the button, or link, of the 'Visit in Settings' notification. Upon clicking, users will be taken to Settings page."
},
"notifications10DescriptionOne": {
"message": "Tính năng phát hiện token cải tiến hiện đã có sẵn trên Mạng chính thức của Ethereum, mạng Polygon, BSC và Avalanche. Sẽ sớm có thêm nhiều mạng khác!"
@ -2154,11 +2191,21 @@
"message": "Bật chế độ tối"
},
"notifications12Description": {
"message": "Chế độ tối sẽ được kích hoạt dành cho người dùng mới tùy theo tùy chọn hệ thống của họ. Đối với người dùng hiện tại, có thể bật Chế độ tối theo cách thủ công trong phần Cài đặt -> Thử nghiệm."
"message": "Tiện ích Chế độ tối hiện đã ra mắt! Để bật, hãy vào Cài đặt -> Thử nghiệm và chọn một trong các tùy chọn hiển thị: Sáng, Tối, Hệ thống."
},
"notifications12Title": {
"message": "Dùng chế độ tối khi nào? Ngay bây giờ! 🕶🦊"
},
"notifications13ActionText": {
"message": "Hiển thị danh sách mạng tùy chỉnh"
},
"notifications13Description": {
"message": "Giờ đây, bạn có thể dễ dàng thêm các mạng tùy chỉnh phổ biến sau: Arbitrum, Avalanche, Binance Smart Chain, Fantom, Harmony, Optimism, Palm và Polygon! Để bật tính năng này, hãy chuyển đến Cài đặt -> Thử nghiệm và bật \"Hiển thị danh sách mạng tùy chỉnh\"!",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "Thêm các mạng phổ biến"
},
"notifications1Description": {
"message": "Giờ đây, người dùng MetaMask trên điện thoại di động có thể hoán đổi token trong ví di động của họ. Quét mã QR để tải ứng dụng di động và bắt đầu hoán đổi.",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2225,7 +2272,7 @@
},
"notifications8ActionText": {
"message": "Đến Cài Đặt Nâng Cao",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "Kể từ phiên bản MetaMask v10.4.0, bạn không cần phần mềm Ledger Live để kết nối thiết bị Ledger của mình với MetaMask nữa.",
@ -2261,10 +2308,6 @@
"notificationsMarkAllAsRead": {
"message": "Đánh dấu đã đọc tất cả"
},
"numberOfNewTokensDetected": {
"message": "Tìm thấy $1 token mới trong tài khoản này",
"description": "$1 is the number of new tokens detected"
},
"ofTextNofM": {
"message": "trên"
},
@ -2344,9 +2387,6 @@
"message": "Mở MetaMask ở chế độ toàn màn hình để kết nối thiết bị Ledger của bạn qua WebHID.",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
},
"openSourceCode": {
"message": "Kiểm tra mã nguồn"
},
"optional": {
"message": "Không bắt buộc"
},
@ -2473,7 +2513,7 @@
},
"preferredLedgerConnectionType": {
"message": "Dạng Kết Nối Ledger Ưu Tiên",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"preparingSwap": {
"message": "Đang chuẩn bị hoán đổi..."
@ -2676,6 +2716,14 @@
"revealTheSeedPhrase": {
"message": "Hiện cụm từ khôi phục bí mật"
},
"revokeAllTokensTitle": {
"message": "Thu hồi quyền truy cập vào tất cả $1 của bạn?",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "Bằng cách thu hồi quyền truy cập, $1 sau đây sẽ không thể truy cập vào $2 của bạn nữa",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Mạng thử nghiệm Rinkeby"
},
@ -2852,12 +2900,23 @@
"message": "Gửi $1",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "Cảnh báo: bạn sắp gửi đến một hợp đồng token và điều này có thể dẫn đến nguy cơ mất tiền. $1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "Thiết lập cài đặt quyền riêng tư nâng cao"
},
"setAdvancedPrivacySettingsDetails": {
"message": "MetaMask sử dụng các dịch vụ của bên thứ ba đáng tin cậy này để nâng cao sự hữu ích và an toàn của sản phẩm."
},
"setApprovalForAll": {
"message": "Cài đặt phê duyệt tất cả"
},
"setApprovalForAllTitle": {
"message": "Phê duyệt $1 không có giới hạn chi tiêu",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "Cài đặt"
},
@ -2877,6 +2936,12 @@
"showAdvancedGasInlineDescription": {
"message": "Chọn tùy chọn này để hiển thị các quyền kiểm soát giá gas và giới hạn ngay trên màn hình gửi và xác nhận."
},
"showCustomNetworkList": {
"message": "Hiển thị danh sách mạng tùy chỉnh"
},
"showCustomNetworkListDescription": {
"message": "Chọn tùy chọn này để hiển thị danh sách các mạng có thông tin được điền sẵn khi thêm mạng mới."
},
"showFiatConversionInTestnets": {
"message": "Hiển thị tỷ lệ quy đổi trên các mạng thử nghiệm"
},
@ -2967,10 +3032,6 @@
"snapInstallWarningCheck": {
"message": "Để xác nhận rằng bạn hiểu, hãy đánh dấu vào tất cả."
},
"snapInstallWarningKeyAccess": {
"message": "Bạn đang cấp quyền truy cập khóa cho Snap \"$1\". Hành động này không thể hủy bỏ và sẽ cấp quyền kiểm soát tài khoản và tài sản của bạn cho \"$1\". Đảm bảo bạn tin tưởng \"$1\" trước khi tiếp tục.",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "Snap này đang yêu cầu các quyền sau:"
},
@ -2986,6 +3047,9 @@
"snapsToggle": {
"message": "Snap chỉ hoạt động khi đã bật"
},
"someNetworksMayPoseSecurity": {
"message": "Một số mạng có thể gây ra rủi ro về bảo mật và/hoặc quyền riêng tư. Bạn cần hiểu rõ các rủi ro này trước khi thêm và sử dụng mạng."
},
"somethingWentWrong": {
"message": "Rất tiếc! Đã xảy ra sự cố."
},
@ -3548,6 +3612,10 @@
"switchNetworks": {
"message": "Chuyển mạng"
},
"switchToNetwork": {
"message": "Chuyển sang $1",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "Chuyển sang tài khoản này"
},
@ -3635,7 +3703,7 @@
},
"toggleTestNetworks": {
"message": "$1 mạng thử nghiệm",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "Token"
@ -3658,18 +3726,6 @@
"tokenDetails": {
"message": "Chi tiết token"
},
"tokenDetection": {
"message": "Phát hiện token"
},
"tokenDetectionAlertMessage": {
"message": "Tính năng phát hiện token hiện có sẵn trên $1. $2"
},
"tokenDetectionAnnouncement": {
"message": "Mới! Tính năng phát hiện token được cải tiến hiện đã có sẵn trên Mạng chính thức của Ethereum dưới dạng một tính năng thử nghiệm. $1"
},
"tokenDetectionToggleDescription": {
"message": "API token của ConsenSys sẽ tổng hợp danh sách token từ các danh sách token của nhiều bên thứ ba khác nhau. Tắt tính năng này sẽ ngừng phát hiện token mới được thêm vào ví của bạn, nhưng sẽ giữ lại tùy chọn tìm kiếm token để nhập."
},
"tokenId": {
"message": "ID Token"
},
@ -3851,6 +3907,9 @@
"unknownCameraErrorTitle": {
"message": "Rất tiếc! Đã xảy ra sự cố...."
},
"unknownCollection": {
"message": "Bộ sưu tập chưa có tên"
},
"unknownNetwork": {
"message": "Mạng riêng không xác định"
},
@ -3901,12 +3960,6 @@
"usePhishingDetectionDescription": {
"message": "Hiển thị cảnh báo đối với các tên miền lừa đảo nhắm đến người dùng Ethereum"
},
"useTokenDetection": {
"message": "Sử Dụng Phát Hiện Token"
},
"useTokenDetectionDescription": {
"message": "Chúng tôi sử dụng API của bên thứ ba để phát hiện và hiển thị các token mới được gửi vào ví của bạn. Hãy tắt tính năng này nếu bạn không muốn MetaMask lấy dữ liệu từ các dịch vụ đó."
},
"useTokenDetectionPrivacyDesc": {
"message": "Tự động hiển thị các token được gửi vào tài khoản của bạn có liên quan đến hoạt động trao đổi thông tin với các máy chủ bên thứ ba để tìm nạp hình ảnh của token. Các máy chủ đó sẽ có quyền truy cập vào địa chỉ IP của bạn."
},
@ -3987,6 +4040,9 @@
"walletCreationSuccessTitle": {
"message": "Tạo ví thành công"
},
"wantToAddThisNetwork": {
"message": "Bạn muốn thêm mạng này?"
},
"warning": {
"message": "Cảnh báo"
},
@ -4049,6 +4105,10 @@
"yesLetsTry": {
"message": "Có, hãy thử"
},
"youHaveAddedAll": {
"message": "Bạn đã thêm tất cả các mạng phổ biến. Bạn có thể khám phá thêm nhiều mạng khác $1 Hoặc bạn có thể $2",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "Bạn cần cho phép truy cập vào máy ảnh để sử dụng tính năng này."
},

@ -157,6 +157,9 @@
"addMemo": {
"message": "添加备忘录"
},
"addMoreNetworks": {
"message": "手动添加更多网络"
},
"addNetwork": {
"message": "添加网络"
},
@ -227,6 +230,10 @@
"alerts": {
"message": "提醒"
},
"allOfYour": {
"message": "您的所有$1",
"description": "$1 is the symbol or name of the token that the user is approving spending"
},
"allowExternalExtensionTo": {
"message": "允许此外部扩展程序:"
},
@ -263,6 +270,10 @@
"approve": {
"message": "批准消费限额"
},
"approveAllTokensTitle": {
"message": "是否允许访问您的所有$1?",
"description": "$1 is the symbol of the token for which the user is granting approval"
},
"approveAndInstall": {
"message": "批准并安装"
},
@ -438,7 +449,7 @@
"message": "使用Wyre购买$1"
},
"buyWithWyreDescription": {
"message": "您可以通过 Wyre 使用借记卡将 $1 存入您的 MetaMask 账户。"
"message": "购买不超过$1000可以轻松开通。快速交互式上限购买验证。支持借记卡/信用卡、Apple Pay、银行转账。适用于100多个国家。代币存入您的MetaMask账户"
},
"bytes": {
"message": "字节"
@ -466,6 +477,13 @@
"message": "若要$1交易,燃料费用必须增加至少10%才能被网络认可。",
"description": "$1 is string 'cancel' or 'speed up'"
},
"cancelSwapForFee": {
"message": "以~$1取消兑换",
"description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction"
},
"cancelSwapForFree": {
"message": "免费取消兑换"
},
"cancellationGasFee": {
"message": "取消燃料费用"
},
@ -1196,9 +1214,6 @@
"failureMessage": {
"message": "出了点问题,我们无法完成此操作"
},
"fakeTokenWarning": {
"message": "任何人都可以创建代币,包括创建现有代币的虚假版本。了解关于 $ 的更多详情"
},
"fast": {
"message": "快"
},
@ -1277,6 +1292,9 @@
"functionApprove": {
"message": "功能:批准"
},
"functionSetApprovalForAll": {
"message": "功能:SetApprovalForAll"
},
"functionType": {
"message": "功能类型"
},
@ -1617,6 +1635,9 @@
"invalidSeedPhrase": {
"message": "助记词无效"
},
"invalidSeedPhraseCaseSensitive": {
"message": "输入无效!助记词须区分大小写。"
},
"ipfsGateway": {
"message": "IPFS 网关"
},
@ -1646,7 +1667,7 @@
"message": "已知合约地址。"
},
"knownTokenWarning": {
"message": "此操作将编辑已经在您的钱包中列出的代币,有能被用来欺骗您。只有确定要更改这些代币的内容时,才通过此操作。了解更多关于 $1"
"message": "此操作将编辑已经在您的钱包中列出的代币,有能被用来欺骗您。只有确定要更改这些代币的内容时,才通过此操作。了解更多关于 $1"
},
"kovan": {
"message": "Kovan 测试网络"
@ -1892,6 +1913,19 @@
"message": "验证网络信息",
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
},
"mismatchedChainRecommendation": {
"message": "我们建议您在继续之前$1。",
"description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key. The link will open to instructions for users to validate custom network details."
},
"mismatchedNetworkName": {
"message": "根据我们的记录,该网络名称可能与此链ID不匹配。"
},
"mismatchedNetworkSymbol": {
"message": "所提交的货币符号与我们对此链ID的预期不匹配。"
},
"mismatchedRpcUrl": {
"message": "根据我们的记录,所提交的RPC URL值与此链ID的已知提供者不匹配。"
},
"missingNFT": {
"message": "找不到您的 NFT?"
},
@ -1943,6 +1977,9 @@
"network": {
"message": "网络: "
},
"networkAddedSuccessfully": {
"message": "网络添加成功!"
},
"networkDetails": {
"message": "网络详情"
},
@ -2059,6 +2096,9 @@
"message": "Nonce 高于建议的 nouce 值 $1",
"description": "The next nonce according to MetaMask's internal logic"
},
"nft": {
"message": "非同质化代币(NFT)"
},
"nftTokenIdPlaceholder": {
"message": "输入代币ID"
},
@ -2154,11 +2194,21 @@
"message": "启用黑暗模式"
},
"notifications12Description": {
"message": "将根据新用户的系统偏好设置为其启用黑暗模式。对于现有用户,请在“设置 -> 实验项”下手动启用黑暗模式。"
"message": "扩展程序的深色模式终于来了!若要开启,请前往“设置 -> 实验项”,然后选择一个显示选项:浅色、深色、系统。"
},
"notifications12Title": {
"message": "何时启用黑暗模式?现在启用黑暗模式! 🕶🦊"
},
"notifications13ActionText": {
"message": "显示自定义网络列表"
},
"notifications13Description": {
"message": "您现在可以轻松添加以下热门自定义网络:Arbitrum、Avalanche、Binance Smart Chain、Fantom、Harmony、Optimism、Palm和Polygon!如需启用此功能,请转到“设置” -> “实验”,然后打开“显示自定义网络列表”!",
"description": "Description of a notification in the 'See What's New' popup. Describes popular network feature."
},
"notifications13Title": {
"message": "添加热门网络"
},
"notifications1Description": {
"message": "MetaMask Mobile 用户现在可以在他们的移动钱包中交换代币。扫描二维码以获取移动应用程序并开始交换。",
"description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature."
@ -2676,6 +2726,14 @@
"revealTheSeedPhrase": {
"message": "显示助记词"
},
"revokeAllTokensTitle": {
"message": "撤销访问您的所有$1的权限?",
"description": "$1 is the symbol of the token for which the user is revoking approval"
},
"revokeApproveForAllDescription": {
"message": "通过撤销权限,以下$1将无法再访问您的$2",
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
},
"rinkeby": {
"message": "Rinkeby 测试网络"
},
@ -2852,12 +2910,23 @@
"message": "正在发送 $1",
"description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
},
"sendingToTokenContractWarning": {
"message": "警告:您将要发送到代币合约,这可能会导致资金损失。$1",
"description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
},
"setAdvancedPrivacySettings": {
"message": "设置高级隐私设置"
},
"setAdvancedPrivacySettingsDetails": {
"message": "MetaMask 使用这些可信的第三方服务来提高产品可用性和安全性。"
},
"setApprovalForAll": {
"message": "设置批准所有"
},
"setApprovalForAllTitle": {
"message": "批准$1,且无消费限制",
"description": "The token symbol that is being approved"
},
"settings": {
"message": "设置"
},
@ -2877,6 +2946,12 @@
"showAdvancedGasInlineDescription": {
"message": "选择此项可直接在发送和确认界面显示燃料价格和上限控制。"
},
"showCustomNetworkList": {
"message": "显示自定义网络列表"
},
"showCustomNetworkListDescription": {
"message": "选择此项,在添加新网络时就会显示附有预填详细信息的网络列表。"
},
"showFiatConversionInTestnets": {
"message": "在测试网络上显示转换"
},
@ -2967,10 +3042,6 @@
"snapInstallWarningCheck": {
"message": "请勾选全部以确认您理解。"
},
"snapInstallWarningKeyAccess": {
"message": "您正在向snap \"$1\"授予密钥访问权限。此操作不可撤销,并会向\"$1\"授予对您的账户和资产的控制权。在继续之前,请确保您信任\"$1\"。",
"description": "The parameter is the name of the snap"
},
"snapRequestsPermission": {
"message": "此Snap正在请求以下权限:"
},
@ -2986,6 +3057,9 @@
"snapsToggle": {
"message": "Snap仅在启用后才会运行"
},
"someNetworksMayPoseSecurity": {
"message": "某些网络可能会带来安全和/或隐私风险。在添加和使用网络之前,请先了解风险。"
},
"somethingWentWrong": {
"message": "哎呀!出了点问题。"
},
@ -3548,6 +3622,10 @@
"switchNetworks": {
"message": "切换网络"
},
"switchToNetwork": {
"message": "切换至$1",
"description": "$1 represents the custom network that has previously been added"
},
"switchToThisAccount": {
"message": "切换到该账户"
},
@ -3664,9 +3742,6 @@
"tokenDetectionAlertMessage": {
"message": "代币检测目前适用于 $1. $2"
},
"tokenDetectionAnnouncement": {
"message": "新功能!以太坊主网上提供了经过改进的代币检测作为实验功能。$1"
},
"tokenDetectionToggleDescription": {
"message": "ConsenSys的代币API使用来自各种第三方的代币列表,汇总成一个代币列表。关闭它将会停止检测添加到您钱包中的新代币,但会保留搜索代币以导入的选项。"
},
@ -3851,6 +3926,9 @@
"unknownCameraErrorTitle": {
"message": "糟糕!出问题了...."
},
"unknownCollection": {
"message": "未命名的收藏"
},
"unknownNetwork": {
"message": "未知的私有网络"
},
@ -3901,12 +3979,6 @@
"usePhishingDetectionDescription": {
"message": "显示针对 Ethereum 用户的网络钓鱼域名警告"
},
"useTokenDetection": {
"message": "使用代币检测"
},
"useTokenDetectionDescription": {
"message": "我们使用第三方 API 来检测和显示发送到您钱包的新代币。如果您不希望 MetaMask 从这些服务中提取数据,请关闭。"
},
"useTokenDetectionPrivacyDesc": {
"message": "要自动显示发送到您账户的代币,需要与第三方服务器通信以获取代币的图像。这些服务器将拥有您的IP地址的访问权限。"
},
@ -3987,6 +4059,9 @@
"walletCreationSuccessTitle": {
"message": "钱包创建成功"
},
"wantToAddThisNetwork": {
"message": "想要添加此网络吗?"
},
"warning": {
"message": "警告"
},
@ -4049,6 +4124,10 @@
"yesLetsTry": {
"message": "是的,我们试一下"
},
"youHaveAddedAll": {
"message": "您已经添加了所有热门网络。您可以探索更多网络$1,或者您可以$2",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youNeedToAllowCameraAccess": {
"message": "需要开启相机访问权限,才能使用该功能。"
},

@ -613,7 +613,7 @@
},
"customGasSettingToolTipMessage": {
"message": "使用$1来定制燃料价格。如果您不熟悉这可能会引起混淆。操作风险自付。",
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight"
},
"customGasSubTitle": {
"message": "提升费用可能会缩短处理时间,但不保证绝对有效。"
@ -1017,9 +1017,6 @@
"failureMessage": {
"message": "出了点问题,我们无法完成这个操作。"
},
"fakeTokenWarning": {
"message": "任何人都可以创建代币,包括创建现有代币的假版本。了解更多关于 $1"
},
"fast": {
"message": "快"
},
@ -1233,7 +1230,7 @@
},
"highGasSettingToolTipMessage": {
"message": "使用$1来覆盖网络流量因像流行的 NFT 丢弃而出现的剧增。",
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold font-weight"
},
"highLowercase": {
"message": "高"
@ -1484,7 +1481,7 @@
},
"lowGasSettingToolTipMessage": {
"message": "使用$1等待较便宜的价格。时间估计远不准确,因为价格有些难以预测。",
"description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'low' separated here so that it can be passed in with bold font-weight"
},
"lowLowercase": {
"message": "低"
@ -1519,7 +1516,7 @@
},
"mediumGasSettingToolTipMessage": {
"message": "使用$1按当前市场价格快速处理。",
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight"
"description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold font-weight"
},
"memo": {
"message": "备忘"
@ -1865,7 +1862,7 @@
},
"notifications8ActionText": {
"message": "转到高级设置",
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced Settings page."
"description": "Description on an action button that appears in the What's New popup. Tells the user that if they click it, they will go to our Advanced settings page."
},
"notifications8DescriptionOne": {
"message": "从MetaMaskv10.4.0开始,您不再需要Ledger Live连接您的Ledger设备到Metamask。",
@ -2039,7 +2036,7 @@
},
"preferredLedgerConnectionType": {
"message": "首选Ledger连接类型",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
"description": "A header for a dropdown in Settings > Advanced. Appears above the ledgerConnectionPreferenceDescription message"
},
"prev": {
"message": "上一个"
@ -2952,7 +2949,7 @@
},
"toggleTestNetworks": {
"message": "$1 测试网络",
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open to the advanced settings where users can enable the display of test networks in the network dropdown."
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
},
"token": {
"message": "代币"
@ -2966,9 +2963,6 @@
"tokenDecimalFetchFailed": {
"message": "需要代币十进制。"
},
"tokenDetectionAnnouncement": {
"message": "新功能!改进的代币检测可以作为实验功能在Ethereum Mainnet上进行。$1"
},
"tokenSymbol": {
"message": "代币符号"
},
@ -3178,12 +3172,6 @@
"usePhishingDetectionDescription": {
"message": "显示针对 Ethereum 用户钓鱼域名的警告。"
},
"useTokenDetection": {
"message": "使用代币检测"
},
"useTokenDetectionDescription": {
"message": "我们使用第三方API来检测和显示发送到您钱包的新代币。 如果您不想从这些服务中拉取数据,请关闭"
},
"usedByClients": {
"message": "可用于各种不同的客户端"
},

@ -0,0 +1,17 @@
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 16.25C12.4183 16.25 16 12.6683 16 8.25C16 3.83172 12.4183 0.25 8 0.25C3.58172 0.25 0 3.83172 0 8.25C0 12.6683 3.58172 16.25 8 16.25Z" fill="white"/>
<g clip-path="url(#clip0_2785_11066)">
<path d="M7.9043 15.25C11.7703 15.25 14.9043 12.116 14.9043 8.25C14.9043 4.38401 11.7703 1.25 7.9043 1.25C4.0383 1.25 0.904297 4.38401 0.904297 8.25C0.904297 12.116 4.0383 15.25 7.9043 15.25Z" fill="#627EEA"/>
<path d="M8.12207 3V6.88063L11.402 8.34625L8.12207 3Z" fill="white" fill-opacity="0.602"/>
<path d="M8.12217 3L4.8418 8.34625L8.12217 6.88063V3Z" fill="white"/>
<path d="M8.12207 10.861V13.4978L11.4042 8.95703L8.12207 10.861Z" fill="white" fill-opacity="0.602"/>
<path d="M8.12217 13.4978V10.8606L4.8418 8.95703L8.12217 13.4978Z" fill="white"/>
<path d="M8.12207 10.2508L11.402 8.34634L8.12207 6.88159V10.2508Z" fill="white" fill-opacity="0.2"/>
<path d="M4.8418 8.34634L8.12217 10.2508V6.88159L4.8418 8.34634Z" fill="white" fill-opacity="0.602"/>
</g>
<defs>
<clipPath id="clip0_2785_11066">
<rect width="14" height="14" fill="white" transform="translate(0.904297 1.25)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -1,4 +1,5 @@
{
"content_security_policy": "frame-ancestors 'none'",
"externally_connectable": {
"matches": ["https://metamask.io/*"],
"ids": ["*"]

@ -47,6 +47,7 @@
],
"default_locale": "en",
"description": "__MSG_appDescription__",
"host_permissions": ["file://*/*", "http://*/*", "https://*/*"],
"icons": {
"16": "images/icon-16.png",
"19": "images/icon-19.png",
@ -61,6 +62,7 @@
"name": "__MSG_appName__",
"permissions": [
"storage",
"scripting",
"unlimitedStorage",
"clipboardWrite",
"http://localhost:8545/",
@ -72,11 +74,5 @@
"*://*.eth/",
"notifications"
],
"short_name": "__MSG_appName__",
"web_accessible_resources": [
{
"resources": ["inpage.js", "phishing.html"],
"matches": ["http://*/*", "https://*/*"]
}
]
"short_name": "__MSG_appName__"
}

@ -1,4 +1,7 @@
{
"content_security_policy": {
"extension_pages": "default-src 'self'; frame-ancestors 'none'"
},
"externally_connectable": {
"matches": ["https://metamask.io/*"],
"ids": ["*"]

@ -1,42 +1,87 @@
/* global chrome */
// This file is used only for manifest version 3
// Represents if importAllScripts has been run
// eslint-disable-next-line
let scriptsLoadInitiated = false;
const testMode = process.env.IN_TEST;
const loadTimeLogs = [];
// eslint-disable-next-line import/unambiguous
function tryImport(...fileNames) {
try {
const startTime = new Date().getTime();
// eslint-disable-next-line
importScripts(...fileNames);
const endTime = new Date().getTime();
loadTimeLogs.push({
name: fileNames[0],
value: endTime - startTime,
children: [],
startTime,
endTime,
});
return true;
} catch (e) {
console.error(e);
return false;
}
return false;
}
function importAllScripts() {
// Bail if we've already imported scripts
if (scriptsLoadInitiated) {
return;
}
scriptsLoadInitiated = true;
const files = [];
// In testMode individual files are imported, this is to help capture load time stats
const loadFile = (fileName) => {
if (testMode) {
tryImport(fileName);
} else {
files.push(fileName);
}
};
const startImportScriptsTime = Date.now();
// value of applyLavaMoat below is dynamically replaced at build time with actual value
const applyLavaMoat = true;
const applyLavaMoat = process.env.APPLY_LAVAMOAT;
if (typeof applyLavaMoat !== 'boolean') {
throw new Error('Missing APPLY_LAVAMOAT environment variable');
}
tryImport('./globalthis.js');
tryImport('./sentry-install.js');
loadFile('./globalthis.js');
loadFile('./sentry-install.js');
if (applyLavaMoat) {
tryImport('./runtime-lavamoat.js');
tryImport('./lockdown-more.js');
tryImport('./policy-load.js');
// Always apply LavaMoat in e2e test builds, so that we can capture initialization stats
if (testMode || applyLavaMoat) {
loadFile('./runtime-lavamoat.js');
loadFile('./lockdown-more.js');
loadFile('./policy-load.js');
} else {
tryImport('./init-globals.js');
tryImport('./lockdown-install.js');
tryImport('./lockdown-run.js');
tryImport('./lockdown-more.js');
tryImport('./runtime-cjs.js');
loadFile('./init-globals.js');
loadFile('./lockdown-install.js');
loadFile('./lockdown-run.js');
loadFile('./lockdown-more.js');
loadFile('./runtime-cjs.js');
}
const fileList = [
// The list of files is injected at build time by replacing comment below with comma separated strings of file names
// https://github.com/MetaMask/metamask-extension/blob/496d9d81c3367931031edc11402552690c771acf/development/build/scripts.js#L406
/** FILE NAMES */
];
// This environment variable is set to a string of comma-separated relative file paths.
const rawFileList = process.env.FILE_NAMES;
const fileList = rawFileList.split(',');
fileList.forEach((fileName) => loadFile(fileName));
// Import all required resources
tryImport(...files);
fileList.forEach((fileName) => tryImport(fileName));
const endImportScriptsTime = Date.now();
// for performance metrics/reference
console.log(
@ -44,7 +89,55 @@ function importAllScripts() {
(Date.now() - startImportScriptsTime) / 1000
}`,
);
// In testMode load time logs are output to console
if (testMode) {
console.log(
`Time for each import: ${JSON.stringify(
{
name: 'Total',
children: loadTimeLogs,
startTime: startImportScriptsTime,
endTime: endImportScriptsTime,
value: endImportScriptsTime - startImportScriptsTime,
version: 1,
},
undefined,
' ',
)}`,
);
}
}
// Placing script import call here ensures that scripts are inported each time service worker is activated.
// Ref: https://stackoverflow.com/questions/66406672/chrome-extension-mv3-modularize-service-worker-js-file
// eslint-disable-next-line no-undef
self.addEventListener('install', importAllScripts);
/*
* A keepalive message listener to prevent Service Worker getting shut down due to inactivity.
* UI sends the message periodically, in a setInterval.
* Chrome will revive the service worker if it was shut down, whenever a new message is sent, but only if a listener was defined here.
*
* chrome below needs to be replaced by cross-browser object,
* but there is issue in importing webextension-polyfill into service worker.
* chrome does seems to work in at-least all chromium based browsers
*/
chrome.runtime.onMessage.addListener(() => {
importAllScripts();
return false;
});
/*
* This content script is injected programmatically because
* MAIN world injection does not work properly via manifest
* https://bugs.chromium.org/p/chromium/issues/detail?id=634381
*/
chrome.scripting.registerContentScripts([
{
id: 'inpage',
matches: ['file://*/*', 'http://*/*', 'https://*/*'],
js: ['inpage.js'],
runAt: 'document_start',
world: 'MAIN',
},
]);

@ -22,6 +22,9 @@ import { SECOND } from '../../shared/constants/time';
import {
REJECT_NOTFICIATION_CLOSE,
REJECT_NOTFICIATION_CLOSE_SIG,
EVENT,
EVENT_NAMES,
TRAITS,
} from '../../shared/constants/metametrics';
import { isManifestV3 } from '../../shared/modules/mv3.utils';
import { maskObject } from '../../shared/modules/object.utils';
@ -69,6 +72,7 @@ let notificationIsOpen = false;
let uiIsTriggering = false;
const openMetamaskTabsIDs = {};
const requestAccountTabIds = {};
let controller;
// state persistence
const inTest = process.env.IN_TEST;
@ -96,7 +100,7 @@ const initApp = async (remotePort) => {
log.info('MetaMask initialization complete.');
};
if (isManifestV3()) {
if (isManifestV3) {
browser.runtime.onConnect.addListener(initApp);
} else {
// initialization flow
@ -314,7 +318,7 @@ function setupController(initState, initLangCode, remoteSourcePort) {
// MetaMask Controller
//
const controller = new MetamaskController({
controller = new MetamaskController({
infuraProjectId: process.env.INFURA_PROJECT_ID,
// User confirmation callbacks:
showUserConfirmation: triggerUi,
@ -398,7 +402,7 @@ function setupController(initState, initLangCode, remoteSourcePort) {
//
// connect to other contexts
//
if (isManifestV3() && remoteSourcePort) {
if (isManifestV3 && remoteSourcePort) {
connectRemote(remoteSourcePort);
}
@ -472,7 +476,7 @@ function setupController(initState, initLangCode, remoteSourcePort) {
controller.isClientOpen = true;
controller.setupTrustedCommunication(portStream, remotePort.sender);
if (isManifestV3()) {
if (isManifestV3) {
// Message below if captured by UI code in app/scripts/ui.js which will trigger UI initialisation
// This ensures that UI is initialised only after background is ready
// It fixes the issue of blank screen coming when extension is loaded, the issue is very frequent in MV3
@ -601,7 +605,7 @@ function setupController(initState, initLangCode, remoteSourcePort) {
label = String(count);
}
// browserAction has been replaced by action in MV3
if (isManifestV3()) {
if (isManifestV3) {
browser.action.setBadgeText({ text: label });
browser.action.setBadgeBackgroundColor({ color: '#037DD6' });
} else {
@ -615,11 +619,11 @@ function setupController(initState, initLangCode, remoteSourcePort) {
const { unapprovedMsgCount } = controller.messageManager;
const { unapprovedPersonalMsgCount } = controller.personalMessageManager;
const { unapprovedDecryptMsgCount } = controller.decryptMessageManager;
const {
unapprovedEncryptionPublicKeyMsgCount,
} = controller.encryptionPublicKeyManager;
const { unapprovedEncryptionPublicKeyMsgCount } =
controller.encryptionPublicKeyManager;
const { unapprovedTypedMessagesCount } = controller.typedMessageManager;
const pendingApprovalCount = controller.approvalController.getTotalApprovalCount();
const pendingApprovalCount =
controller.approvalController.getTotalApprovalCount();
const waitingForUnlockCount =
controller.appStateController.waitingForUnlock.length;
return (
@ -751,12 +755,32 @@ async function openPopup() {
});
}
// It adds the "App Installed" event into a queue of events, which will be tracked only after a user opts into metrics.
const addAppInstalledEvent = () => {
if (controller) {
controller.metaMetricsController.updateTraits({
[TRAITS.INSTALL_DATE_EXT]: new Date().toISOString().split('T')[0], // yyyy-mm-dd
});
controller.metaMetricsController.addEventBeforeMetricsOptIn({
category: EVENT.CATEGORIES.APP,
event: EVENT_NAMES.APP_INSTALLED,
properties: {},
});
return;
}
setTimeout(() => {
// If the controller is not set yet, we wait and try to add the "App Installed" event again.
addAppInstalledEvent();
}, 1000);
};
// On first install, open a new tab with MetaMask
browser.runtime.onInstalled.addListener(({ reason }) => {
if (
reason === 'install' &&
!(process.env.METAMASK_DEBUG || process.env.IN_TEST)
) {
addAppInstalledEvent();
platform.openExtensionInBrowser();
}
});

@ -6,3 +6,19 @@ export const SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN =
'0xb8e671734ce5c8d7dfbbea5574fa4cf39f7a54a4';
export const SINGLE_CALL_BALANCES_ADDRESS_KOVAN =
'0xb1d3fbb2f83aecd196f474c16ca5d9cffa0d0ffc';
export const SINGLE_CALL_BALANCES_ADDRESS_GOERLI =
'0x9788C4E93f9002a7ad8e72633b11E8d1ecd51f9b';
// TODO(SEPOLIA) There is currently no balance call address for Sepolia
export const SINGLE_CALL_BALANCES_ADDRESS_SEPOLIA = '';
export const SINGLE_CALL_BALANCES_ADDRESS_BSC =
'0x2352c63A83f9Fd126af8676146721Fa00924d7e4';
export const SINGLE_CALL_BALANCES_ADDRESS_OPTIMISM =
'0xB1c568e9C3E6bdaf755A60c7418C269eb11524FC';
export const SINGLE_CALL_BALANCES_ADDRESS_POLYGON =
'0x2352c63A83f9Fd126af8676146721Fa00924d7e4';
export const SINGLE_CALL_BALANCES_ADDRESS_AVALANCHE =
'0xD023D153a0DFa485130ECFdE2FAA7e612EF94818';
export const SINGLE_CALL_BALANCES_ADDRESS_FANTOM =
'0x07f697424ABe762bB808c109860c04eA488ff92B';
export const SINGLE_CALL_BALANCES_ADDRESS_ARBITRUM =
'0x151E24A486D7258dd7C33Fb67E4bB01919B7B32c';

@ -1,4 +1,3 @@
import querystring from 'querystring';
import pump from 'pump';
import { WindowPostMessageStream } from '@metamask/post-message-stream';
import ObjectMultiplex from 'obj-multiplex';
@ -7,6 +6,7 @@ import PortStream from 'extension-port-stream';
import { obj as createThoughStream } from 'through2';
import { isManifestV3 } from '../../shared/modules/mv3.utils';
import shouldInjectProvider from '../../shared/modules/provider-injection';
// These require calls need to use require to be statically recognized by browserify
const fs = require('fs');
@ -28,6 +28,8 @@ const PHISHING_WARNING_PAGE = 'metamask-phishing-warning-page';
const PHISHING_SAFELIST = 'metamask-phishing-safelist';
const PROVIDER = 'metamask-provider';
// For more information about these legacy streams, see here:
// https://github.com/MetaMask/metamask-extension/issues/15491
// TODO:LegacyProvider: Delete
const LEGACY_CONTENT_SCRIPT = 'contentscript';
const LEGACY_INPAGE = 'inpage';
@ -42,7 +44,9 @@ if (
) {
setupPhishingStream();
} else if (shouldInjectProvider()) {
if (!isManifestV3) {
injectScript(inpageBundle);
}
setupStreams();
}
@ -56,12 +60,7 @@ function injectScript(content) {
const container = document.head || document.documentElement;
const scriptTag = document.createElement('script');
scriptTag.setAttribute('async', 'false');
// Inline scripts do not work in MV3 due to more strict security policy
if (isManifestV3()) {
scriptTag.setAttribute('src', browser.runtime.getURL('inpage.js'));
} else {
scriptTag.textContent = content;
}
container.insertBefore(scriptTag, container.children[0]);
container.removeChild(scriptTag);
} catch (error) {
@ -263,108 +262,17 @@ function notifyInpageOfStreamFailure() {
);
}
/**
* Determines if the provider should be injected
*
* @returns {boolean} {@code true} Whether the provider should be injected
*/
function shouldInjectProvider() {
return (
doctypeCheck() &&
suffixCheck() &&
documentElementCheck() &&
!blockedDomainCheck()
);
}
/**
* Checks the doctype of the current document if it exists
*
* @returns {boolean} {@code true} if the doctype is html or if none exists
*/
function doctypeCheck() {
const { doctype } = window.document;
if (doctype) {
return doctype.name === 'html';
}
return true;
}
/**
* Returns whether or not the extension (suffix) of the current document is prohibited
*
* This checks {@code window.location.pathname} against a set of file extensions
* that we should not inject the provider into. This check is indifferent of
* query parameters in the location.
*
* @returns {boolean} whether or not the extension of the current document is prohibited
*/
function suffixCheck() {
const prohibitedTypes = [/\.xml$/u, /\.pdf$/u];
const currentUrl = window.location.pathname;
for (let i = 0; i < prohibitedTypes.length; i++) {
if (prohibitedTypes[i].test(currentUrl)) {
return false;
}
}
return true;
}
/**
* Checks the documentElement of the current document
*
* @returns {boolean} {@code true} if the documentElement is an html node or if none exists
*/
function documentElementCheck() {
const documentElement = document.documentElement.nodeName;
if (documentElement) {
return documentElement.toLowerCase() === 'html';
}
return true;
}
/**
* Checks if the current domain is blocked
*
* @returns {boolean} {@code true} if the current domain is blocked
*/
function blockedDomainCheck() {
const blockedDomains = [
'adyen.com',
'ani.gamer.com.tw',
'blueskybooking.com',
'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html',
'docs.google.com',
'dropbox.com',
'gravityforms.com',
'harbourair.com',
'sharefile.com',
'uscourts.gov',
'webbyawards.com',
];
const currentUrl = window.location.href;
let currentRegex;
for (let i = 0; i < blockedDomains.length; i++) {
const blockedDomain = blockedDomains[i].replace('.', '\\.');
currentRegex = new RegExp(
`(?:https?:\\/\\/)(?:(?!${blockedDomain}).)*$`,
'u',
);
if (!currentRegex.test(currentUrl)) {
return true;
}
}
return false;
}
/**
* Redirects the current page to a phishing information page
*
* @param data
*/
function redirectToPhishingWarning() {
function redirectToPhishingWarning(data = {}) {
console.debug('MetaMask: Routing to Phishing Warning page.');
const { hostname, href } = window.location;
const { newIssueUrl } = data;
const baseUrl = process.env.PHISHING_WARNING_PAGE_URL;
window.location.href = `${baseUrl}#${querystring.stringify({
hostname: window.location.hostname,
href: window.location.href,
})}`;
const querystring = new URLSearchParams({ hostname, href, newIssueUrl });
window.location.href = `${baseUrl}#${querystring}`;
}

@ -33,10 +33,19 @@ export default class AppStateController extends EventEmitter {
collectiblesDetectionNoticeDismissed: false,
enableEIP1559V2NoticeDismissed: false,
showTestnetMessageInDropdown: true,
showPortfolioTooltip: true,
trezorModel: null,
...initState,
qrHardware: {},
collectiblesDropdownState: {},
usedNetworks: {
'0x1': true,
'0x2a': true,
'0x3': true,
'0x4': true,
'0x5': true,
'0x539': true,
},
});
this.timer = null;
@ -251,6 +260,15 @@ export default class AppStateController extends EventEmitter {
this.store.updateState({ showTestnetMessageInDropdown });
}
/**
* Sets whether the portfolio site tooltip should be shown on the home page
*
* @param showPortfolioTooltip
*/
setShowPortfolioTooltip(showPortfolioTooltip) {
this.store.updateState({ showPortfolioTooltip });
}
/**
* Sets a property indicating the model of the user's Trezor hardware wallet
*
@ -294,4 +312,18 @@ export default class AppStateController extends EventEmitter {
collectiblesDropdownState,
});
}
/**
* Updates the array of the first time used networks
*
* @param chainId
* @returns {void}
*/
setFirstTimeUsedNetwork(chainId) {
const currentState = this.store.getState();
const { usedNetworks } = currentState;
usedNetworks[chainId] = true;
this.store.updateState({ usedNetworks });
}
}

@ -0,0 +1,77 @@
import { exportAsFile } from '../../../shared/modules/export-utils';
import { prependZero } from '../../../shared/modules/string-utils';
export default class BackupController {
constructor(opts = {}) {
const {
preferencesController,
addressBookController,
trackMetaMetricsEvent,
} = opts;
this.preferencesController = preferencesController;
this.addressBookController = addressBookController;
this._trackMetaMetricsEvent = trackMetaMetricsEvent;
}
async restoreUserData(jsonString) {
const existingPreferences = this.preferencesController.store.getState();
const { preferences, addressBook } = JSON.parse(jsonString);
if (preferences) {
preferences.identities = existingPreferences.identities;
preferences.lostIdentities = existingPreferences.lostIdentities;
preferences.selectedAddress = existingPreferences.selectedAddress;
this.preferencesController.store.updateState(preferences);
}
if (addressBook) {
this.addressBookController.update(addressBook, true);
}
if (preferences && addressBook) {
this._trackMetaMetricsEvent({
event: 'User Data Imported',
category: 'Backup',
});
}
}
async backupUserData() {
const userData = {
preferences: { ...this.preferencesController.store.getState() },
addressBook: { ...this.addressBookController.state },
};
/**
* We can remove these properties since we will won't be restoring identities from backup
*/
delete userData.preferences.identities;
delete userData.preferences.lostIdentities;
delete userData.preferences.selectedAddress;
const result = JSON.stringify(userData);
const date = new Date();
const prefixZero = (num) => prependZero(num, 2);
/*
* userData.YYYY_MM_DD_HH_mm_SS e.g userData.2022_01_13_13_45_56
* */
const userDataFileName = `MetaMaskUserData.${date.getFullYear()}_${prefixZero(
date.getMonth() + 1,
)}_${prefixZero(date.getDay())}_${prefixZero(date.getHours())}_${prefixZero(
date.getMinutes(),
)}_${prefixZero(date.getDay())}.json`;
exportAsFile(userDataFileName, result);
this._trackMetaMetricsEvent({
event: 'User Data Exported',
category: 'Backup',
});
return result;
}
}

@ -0,0 +1,118 @@
import { strict as assert } from 'assert';
import sinon from 'sinon';
import BackupController from './backup';
function getMockController() {
const mcState = {
getSelectedAddress: sinon.stub().returns('0x01'),
selectedAddress: '0x01',
identities: {
'0x295e26495CEF6F69dFA69911d9D8e4F3bBadB89B': {
address: '0x295e26495CEF6F69dFA69911d9D8e4F3bBadB89B',
lastSelected: 1655380342907,
name: 'Account 3',
},
},
lostIdentities: {
'0xfd59bbe569376e3d3e4430297c3c69ea93f77435': {
address: '0xfd59bbe569376e3d3e4430297c3c69ea93f77435',
lastSelected: 1655379648197,
name: 'Ledger 1',
},
},
update: (store) => (mcState.store = store),
};
mcState.store = {
getState: sinon.stub().returns(mcState),
updateState: (store) => (mcState.store = store),
};
return mcState;
}
const jsonData = `{"preferences":{"frequentRpcListDetail":[{"chainId":"0x539","nickname":"Localhost 8545","rpcPrefs":{},"rpcUrl":"http://localhost:8545","ticker":"ETH"},{"chainId":"0x38","nickname":"Binance Smart Chain Mainnet","rpcPrefs":{"blockExplorerUrl":"https://bscscan.com"},"rpcUrl":"https://bsc-dataseed1.binance.org","ticker":"BNB"},{"chainId":"0x61","nickname":"Binance Smart Chain Testnet","rpcPrefs":{"blockExplorerUrl":"https://testnet.bscscan.com"},"rpcUrl":"https://data-seed-prebsc-1-s1.binance.org:8545","ticker":"tBNB"},{"chainId":"0x89","nickname":"Polygon Mainnet","rpcPrefs":{"blockExplorerUrl":"https://polygonscan.com"},"rpcUrl":"https://polygon-rpc.com","ticker":"MATIC"}],"useBlockie":false,"useNonceField":false,"usePhishDetect":true,"dismissSeedBackUpReminder":false,"useTokenDetection":false,"useCollectibleDetection":false,"openSeaEnabled":false,"advancedGasFee":null,"featureFlags":{"sendHexData":true,"showIncomingTransactions":true},"knownMethodData":{},"currentLocale":"en","forgottenPassword":false,"preferences":{"hideZeroBalanceTokens":false,"showFiatInTestnets":false,"showTestNetworks":true,"useNativeCurrencyAsPrimaryCurrency":true},"ipfsGateway":"dweb.link","infuraBlocked":false,"ledgerTransportType":"webhid","theme":"light","customNetworkListEnabled":false,"textDirection":"auto"},"addressBook":{"addressBook":{"0x61":{"0x42EB768f2244C8811C63729A21A3569731535f06":{"address":"0x42EB768f2244C8811C63729A21A3569731535f06","chainId":"0x61","isEns":false,"memo":"","name":""}}}}}`;
describe('BackupController', function () {
const getBackupController = () => {
return new BackupController({
preferencesController: getMockController(),
addressBookController: getMockController(),
trackMetaMetricsEvent: sinon.stub(),
});
};
describe('constructor', function () {
it('should setup correctly', async function () {
const backupController = getBackupController();
const selectedAddress =
backupController.preferencesController.getSelectedAddress();
assert.equal(selectedAddress, '0x01');
});
it('should restore backup', async function () {
const backupController = getBackupController();
backupController.restoreUserData(jsonData);
// check Preferences backup
assert.equal(
backupController.preferencesController.store.frequentRpcListDetail[0]
.chainId,
'0x539',
);
assert.equal(
backupController.preferencesController.store.frequentRpcListDetail[1]
.chainId,
'0x38',
);
// make sure identities are not lost after restore
assert.equal(
backupController.preferencesController.store.identities[
'0x295e26495CEF6F69dFA69911d9D8e4F3bBadB89B'
].lastSelected,
1655380342907,
);
assert.equal(
backupController.preferencesController.store.identities[
'0x295e26495CEF6F69dFA69911d9D8e4F3bBadB89B'
].name,
'Account 3',
);
assert.equal(
backupController.preferencesController.store.lostIdentities[
'0xfd59bbe569376e3d3e4430297c3c69ea93f77435'
].lastSelected,
1655379648197,
);
assert.equal(
backupController.preferencesController.store.lostIdentities[
'0xfd59bbe569376e3d3e4430297c3c69ea93f77435'
].name,
'Ledger 1',
);
// make sure selected address is not lost after restore
assert.equal(
backupController.preferencesController.store.selectedAddress,
'0x01',
);
// check address book backup
assert.equal(
backupController.addressBookController.store.addressBook['0x61'][
'0x42EB768f2244C8811C63729A21A3569731535f06'
].chainId,
'0x61',
);
assert.equal(
backupController.addressBookController.store.addressBook['0x61'][
'0x42EB768f2244C8811C63729A21A3569731535f06'
].address,
'0x42EB768f2244C8811C63729A21A3569731535f06',
);
assert.equal(
backupController.addressBookController.store.addressBook['0x61'][
'0x42EB768f2244C8811C63729A21A3569731535f06'
].isEns,
false,
);
});
});
});

@ -1,13 +1,14 @@
import Web3 from 'web3';
import { warn } from 'loglevel';
import SINGLE_CALL_BALANCES_ABI from 'single-call-balance-checker-abi';
import { SINGLE_CALL_BALANCES_ADDRESS } from '../constants/contracts';
import { MINUTE } from '../../../shared/constants/time';
import { MAINNET_CHAIN_ID } from '../../../shared/constants/network';
import { STATIC_MAINNET_TOKEN_LIST } from '../../../shared/constants/tokens';
import { isTokenDetectionEnabledForNetwork } from '../../../shared/modules/network.utils';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
import { TOKEN_STANDARDS } from '../../../ui/helpers/constants/common';
import { ASSET_TYPES } from '../../../shared/constants/transaction';
import {
ASSET_TYPES,
TOKEN_STANDARDS,
} from '../../../shared/constants/transaction';
import { EVENT, EVENT_NAMES } from '../../../shared/constants/metametrics';
// By default, poll every 3 minutes
@ -48,14 +49,15 @@ export default class DetectTokensController {
this.network = network;
this.keyringMemStore = keyringMemStore;
this.tokenList = tokenList;
this.useTokenDetection =
this.preferences?.store.getState().useTokenDetection;
this.selectedAddress = this.preferences?.store.getState().selectedAddress;
this.tokenAddresses = this.tokensController?.state.tokens.map((token) => {
return token.address;
});
this.hiddenTokens = this.tokensController?.state.ignoredTokens;
this.detectedTokens = process.env.TOKEN_DETECTION_V2
? this.tokensController?.state.detectedTokens
: [];
this.detectedTokens = this.tokensController?.state.detectedTokens;
this.chainId = this.getChainIdFromNetworkStore(network);
this._trackMetaMetricsEvent = trackMetaMetricsEvent;
preferences?.store.subscribe(({ selectedAddress, useTokenDetection }) => {
@ -74,32 +76,11 @@ export default class DetectTokensController {
return token.address;
});
this.hiddenTokens = ignoredTokens;
this.detectedTokens = process.env.TOKEN_DETECTION_V2
? detectedTokens
: [];
this.detectedTokens = detectedTokens;
},
);
}
/**
* TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up
*
* @param tokens
*/
async _getTokenBalances(tokens) {
const ethContract = this.web3.eth
.contract(SINGLE_CALL_BALANCES_ABI)
.at(SINGLE_CALL_BALANCES_ADDRESS);
return new Promise((resolve, reject) => {
ethContract.balances([this.selectedAddress], tokens, (error, result) => {
if (error) {
return reject(error);
}
return resolve(result);
});
});
}
/**
* For each token in the tokenlist provided by the TokenListController, check selectedAddress balance.
*/
@ -108,31 +89,33 @@ export default class DetectTokensController {
return;
}
if (
process.env.TOKEN_DETECTION_V2 &&
(!this.useTokenDetection ||
!isTokenDetectionEnabledForNetwork(
this._network.store.getState().provider.chainId,
))
this.getChainIdFromNetworkStore(this._network),
)
) {
return;
}
const { tokenList } = this._tokenList.state;
// since the token detection is currently enabled only on Mainnet
// we can use the chainId check to ensure token detection is not triggered for any other network
// but once the balance check contract for other networks are deploayed and ready to use, we need to update this check.
if (
!process.env.TOKEN_DETECTION_V2 &&
(this._network.store.getState().provider.chainId !== MAINNET_CHAIN_ID ||
Object.keys(tokenList).length === 0)
!this.useTokenDetection &&
this.getChainIdFromNetworkStore(this._network) !== MAINNET_CHAIN_ID
) {
return;
}
const isTokenDetectionInactiveInMainnet =
!this.useTokenDetection &&
this.getChainIdFromNetworkStore(this._network) === MAINNET_CHAIN_ID;
const { tokenList } = this._tokenList.state;
const tokenListUsed = isTokenDetectionInactiveInMainnet
? STATIC_MAINNET_TOKEN_LIST
: tokenList;
const tokensToDetect = [];
this.web3.setProvider(this._network._provider);
for (const tokenAddress in tokenList) {
for (const tokenAddress in tokenListUsed) {
if (
!this.tokenAddresses.find((address) =>
!this.tokenAddresses.find(({ address }) =>
isEqualCaseInsensitive(address, tokenAddress),
) &&
!this.hiddenTokens.find((address) =>
@ -152,12 +135,10 @@ export default class DetectTokensController {
for (const tokensSlice of sliceOfTokensToDetect) {
let result;
try {
result = process.env.TOKEN_DETECTION_V2
? await this.assetsContractController.getBalancesInSingleCall(
result = await this.assetsContractController.getBalancesInSingleCall(
this.selectedAddress,
tokensSlice,
)
: await this._getTokenBalances(tokensSlice);
);
} catch (error) {
warn(
`MetaMask - DetectTokensController single call balance fetch failed`,
@ -166,19 +147,13 @@ export default class DetectTokensController {
return;
}
let tokensWithBalance = [];
if (process.env.TOKEN_DETECTION_V2) {
const tokensWithBalance = [];
const eventTokensDetails = [];
if (result) {
const nonZeroTokenAddresses = Object.keys(result);
for (const nonZeroTokenAddress of nonZeroTokenAddresses) {
const {
address,
symbol,
decimals,
iconUrl,
aggregators,
} = tokenList[nonZeroTokenAddress];
const { address, symbol, decimals } =
tokenListUsed[nonZeroTokenAddress];
eventTokensDetails.push(`${symbol} - ${address}`);
@ -186,8 +161,6 @@ export default class DetectTokensController {
address,
symbol,
decimals,
image: iconUrl,
aggregators,
});
}
@ -204,21 +177,6 @@ export default class DetectTokensController {
await this.tokensController.addDetectedTokens(tokensWithBalance);
}
}
} else {
tokensWithBalance = tokensSlice.filter((_, index) => {
const balance = result[index];
return balance && !balance.isZero();
});
await Promise.all(
tokensWithBalance.map((tokenAddress) => {
return this.tokensController.addToken(
tokenAddress,
tokenList[tokenAddress].symbol,
tokenList[tokenAddress].decimals,
);
}),
);
}
}
}
@ -235,6 +193,10 @@ export default class DetectTokensController {
this.interval = DEFAULT_INTERVAL;
}
getChainIdFromNetworkStore(network) {
return network?.store.getState().provider.chainId;
}
/* eslint-disable accessor-pairs */
/**
* @type {number}
@ -258,6 +220,12 @@ export default class DetectTokensController {
}
this._network = network;
this.web3 = new Web3(network._provider);
this._network.store.subscribe(() => {
if (this.chainId !== this.getChainIdFromNetworkStore(network)) {
this.restartTokenDetection();
this.chainId = this.getChainIdFromNetworkStore(network);
}
});
}
/**

@ -7,6 +7,7 @@ import {
ControllerMessenger,
TokenListController,
TokensController,
AssetsContractController,
} from '@metamask/controllers';
import { MAINNET, ROPSTEN } from '../../../shared/constants/network';
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
@ -15,9 +16,14 @@ import NetworkController from './network';
import PreferencesController from './preferences';
describe('DetectTokensController', function () {
let tokenListController;
const sandbox = sinon.createSandbox();
let keyringMemStore, network, preferences, provider, tokensController;
let assetsContractController,
keyringMemStore,
network,
preferences,
provider,
tokensController,
tokenListController;
const noop = () => undefined;
@ -31,18 +37,44 @@ describe('DetectTokensController', function () {
network.setInfuraProjectId('foo');
network.initializeProvider(networkControllerProviderConfig);
provider = network.getProviderAndBlockTracker().provider;
preferences = new PreferencesController({ network, provider });
tokensController = new TokensController({
onPreferencesStateChange: preferences.store.subscribe.bind(
preferences.store,
),
onNetworkStateChange: network.store.subscribe.bind(network.store),
const tokenListMessenger = new ControllerMessenger().getRestricted({
name: 'TokenListController',
});
tokenListController = new TokenListController({
chainId: '1',
preventPollingOnNetworkRestart: false,
onNetworkStateChange: sinon.spy(),
onPreferencesStateChange: sinon.spy(),
messenger: tokenListMessenger,
});
await tokenListController.start();
preferences = new PreferencesController({
network,
provider,
tokenListController,
});
preferences.setAddresses([
'0x7e57e2',
'0xbc86727e770de68b1060c91f6bb6945c73e10388',
]);
preferences.setUseTokenDetection(true);
tokensController = new TokensController({
onPreferencesStateChange: preferences.store.subscribe.bind(
preferences.store,
),
onNetworkStateChange: network.store.subscribe.bind(network.store),
});
assetsContractController = new AssetsContractController({
onPreferencesStateChange: preferences.store.subscribe.bind(
preferences.store,
),
onNetworkStateChange: network.store.subscribe.bind(network.store),
});
sandbox
.stub(network, 'getLatestBlock')
.callsFake(() => Promise.resolve({}));
@ -122,17 +154,6 @@ describe('DetectTokensController', function () {
.get(`/tokens/3`)
.reply(200, { error: 'ChainId 3 is not supported' })
.persist();
const tokenListMessenger = new ControllerMessenger().getRestricted({
name: 'TokenListController',
});
tokenListController = new TokenListController({
chainId: '1',
useStaticTokenList: false,
onNetworkStateChange: sinon.spy(),
onPreferencesStateChange: sinon.spy(),
messenger: tokenListMessenger,
});
await tokenListController.start();
});
after(function () {
@ -155,6 +176,7 @@ describe('DetectTokensController', function () {
keyringMemStore,
tokenList: tokenListController,
tokensController,
assetsContractController,
});
controller.isOpen = true;
controller.isUnlocked = true;
@ -171,7 +193,7 @@ describe('DetectTokensController', function () {
sandbox.assert.calledThrice(stub);
});
it('should not check tokens while on test network', async function () {
it('should not check and add tokens while on unsupported networks', async function () {
sandbox.useFakeTimers();
network.setProviderType(ROPSTEN);
const tokenListMessengerRopsten = new ControllerMessenger().getRestricted({
@ -179,7 +201,6 @@ describe('DetectTokensController', function () {
});
tokenListController = new TokenListController({
chainId: '3',
useStaticTokenList: false,
onNetworkStateChange: sinon.spy(),
onPreferencesStateChange: sinon.spy(),
messenger: tokenListMessengerRopsten,
@ -191,17 +212,21 @@ describe('DetectTokensController', function () {
keyringMemStore,
tokenList: tokenListController,
tokensController,
assetsContractController,
});
controller.isOpen = true;
controller.isUnlocked = true;
const stub = sandbox.stub(controller, '_getTokenBalances');
const stub = sandbox.stub(
assetsContractController,
'getBalancesInSingleCall',
);
await controller.detectNewTokens();
sandbox.assert.notCalled(stub);
});
it('should skip adding tokens listed in hiddenTokens array', async function () {
it('should skip adding tokens listed in ignoredTokens array', async function () {
sandbox.useFakeTimers();
network.setProviderType(MAINNET);
const controller = new DetectTokensController({
@ -210,52 +235,49 @@ describe('DetectTokensController', function () {
keyringMemStore,
tokenList: tokenListController,
tokensController,
assetsContractController,
trackMetaMetricsEvent: noop,
});
controller.isOpen = true;
controller.isUnlocked = true;
const { tokenList } = tokenListController.state;
const erc20ContractAddresses = Object.keys(tokenList);
const existingTokenAddress = erc20ContractAddresses[0];
const existingToken = tokenList[existingTokenAddress];
await tokensController.addToken(
existingTokenAddress,
existingToken.symbol,
existingToken.decimals,
);
const tokenValues = Object.values(tokenList);
const tokenAddressToSkip = erc20ContractAddresses[1];
const tokenToSkip = tokenList[tokenAddressToSkip];
await tokensController.addToken(
tokenAddressToSkip,
tokenToSkip.symbol,
tokenToSkip.decimals,
);
await tokensController.addDetectedTokens([
{
address: tokenValues[0].address,
symbol: tokenValues[0].symbol,
decimals: tokenValues[0].decimals,
aggregators: undefined,
image: undefined,
isERC721: undefined,
},
]);
sandbox
.stub(controller, '_getTokenBalances')
.stub(assetsContractController, 'getBalancesInSingleCall')
.callsFake((tokensToDetect) =>
tokensToDetect.map((token) =>
token === tokenAddressToSkip ? new BigNumber(10) : 0,
token.address === tokenValues[1].address ? new BigNumber(10) : 0,
),
);
await tokensController.ignoreTokens([tokenValues[1].address]);
await tokensController.removeAndIgnoreToken(tokenAddressToSkip);
await controller.detectNewTokens();
assert.deepEqual(tokensController.state.tokens, [
assert.deepEqual(tokensController.state.detectedTokens, [
{
address: toChecksumHexAddress(existingTokenAddress),
decimals: existingToken.decimals,
symbol: existingToken.symbol,
address: toChecksumHexAddress(tokenValues[0].address),
decimals: tokenValues[0].decimals,
symbol: tokenValues[0].symbol,
aggregators: undefined,
image: undefined,
isERC721: false,
isERC721: undefined,
},
]);
});
it('should check and add tokens while on main network', async function () {
it('should check and add tokens while on supported networks', async function () {
sandbox.useFakeTimers();
network.setProviderType(MAINNET);
const controller = new DetectTokensController({
@ -264,6 +286,8 @@ describe('DetectTokensController', function () {
keyringMemStore,
tokenList: tokenListController,
tokensController,
assetsContractController,
trackMetaMetricsEvent: noop,
});
controller.isOpen = true;
controller.isUnlocked = true;
@ -273,105 +297,41 @@ describe('DetectTokensController', function () {
const existingTokenAddress = erc20ContractAddresses[0];
const existingToken = tokenList[existingTokenAddress];
await tokensController.addToken(
existingTokenAddress,
existingToken.symbol,
existingToken.decimals,
);
const tokenAddressToAdd = erc20ContractAddresses[1];
const tokenToAdd = tokenList[tokenAddressToAdd];
const contractAddressesToDetect = erc20ContractAddresses.filter(
(address) => address !== existingTokenAddress,
);
const indexOfTokenToAdd = contractAddressesToDetect.indexOf(
tokenAddressToAdd,
);
const balances = new Array(contractAddressesToDetect.length);
balances[indexOfTokenToAdd] = new BigNumber(10);
sandbox
.stub(controller, '_getTokenBalances')
.returns(Promise.resolve(balances));
await controller.detectNewTokens();
assert.deepEqual(tokensController.state.tokens, [
await tokensController.addDetectedTokens([
{
address: toChecksumHexAddress(existingTokenAddress),
decimals: existingToken.decimals,
address: existingToken.address,
symbol: existingToken.symbol,
isERC721: false,
image: undefined,
},
{
address: toChecksumHexAddress(tokenAddressToAdd),
decimals: tokenToAdd.decimals,
symbol: tokenToAdd.symbol,
decimals: existingToken.decimals,
aggregators: undefined,
image: undefined,
isERC721: false,
isERC721: undefined,
},
]);
});
it('should check and add tokens while on non-default Mainnet', async function () {
sandbox.useFakeTimers();
network.setRpcTarget('https://some-fake-RPC-endpoint.metamask.io', '0x1');
const controller = new DetectTokensController({
preferences,
network,
keyringMemStore,
tokenList: tokenListController,
tokensController,
});
controller.isOpen = true;
controller.isUnlocked = true;
const { tokenList } = tokenListController.state;
const erc20ContractAddresses = Object.keys(tokenList);
const existingTokenAddress = erc20ContractAddresses[0];
const existingToken = tokenList[existingTokenAddress];
await tokensController.addToken(
existingTokenAddress,
existingToken.symbol,
existingToken.decimals,
);
const tokenAddressToAdd = erc20ContractAddresses[1];
const tokenToAdd = tokenList[tokenAddressToAdd];
const contractAddressesToDetect = erc20ContractAddresses.filter(
(address) => address !== existingTokenAddress,
);
const indexOfTokenToAdd = contractAddressesToDetect.indexOf(
tokenAddressToAdd,
);
const balances = new Array(contractAddressesToDetect.length);
balances[indexOfTokenToAdd] = new BigNumber(10);
sandbox
.stub(controller, '_getTokenBalances')
.returns(Promise.resolve(balances));
.stub(assetsContractController, 'getBalancesInSingleCall')
.callsFake(() =>
Promise.resolve({ [tokenAddressToAdd]: new BigNumber(10) }),
);
await controller.detectNewTokens();
assert.deepEqual(tokensController.state.tokens, [
assert.deepEqual(tokensController.state.detectedTokens, [
{
address: toChecksumHexAddress(existingTokenAddress),
decimals: existingToken.decimals,
symbol: existingToken.symbol,
aggregators: undefined,
image: undefined,
isERC721: false,
isERC721: undefined,
},
{
address: toChecksumHexAddress(tokenAddressToAdd),
decimals: tokenToAdd.decimals,
symbol: tokenToAdd.symbol,
aggregators: undefined,
image: undefined,
isERC721: false,
isERC721: undefined,
},
]);
});
@ -384,6 +344,7 @@ describe('DetectTokensController', function () {
keyringMemStore,
tokenList: tokenListController,
tokensController,
assetsContractController,
});
controller.isOpen = true;
controller.isUnlocked = true;
@ -402,6 +363,7 @@ describe('DetectTokensController', function () {
keyringMemStore,
tokenList: tokenListController,
tokensController,
assetsContractController,
});
controller.isOpen = true;
controller.selectedAddress = '0x0';
@ -419,10 +381,14 @@ describe('DetectTokensController', function () {
keyringMemStore,
tokenList: tokenListController,
tokensController,
assetsContractController,
});
controller.isOpen = true;
controller.isUnlocked = false;
const stub = sandbox.stub(controller, '_getTokenBalances');
const stub = sandbox.stub(
assetsContractController,
'getBalancesInSingleCall',
);
clock.tick(180000);
sandbox.assert.notCalled(stub);
});
@ -435,6 +401,7 @@ describe('DetectTokensController', function () {
network,
keyringMemStore,
tokensController,
assetsContractController,
});
// trigger state update from preferences controller
await preferences.setSelectedAddress(
@ -442,7 +409,10 @@ describe('DetectTokensController', function () {
);
controller.isOpen = false;
controller.isUnlocked = true;
const stub = sandbox.stub(controller, '_getTokenBalances');
const stub = sandbox.stub(
assetsContractController,
'getBalancesInSingleCall',
);
clock.tick(180000);
sandbox.assert.notCalled(stub);
});

@ -1,5 +1,6 @@
import EthJsEns from 'ethjs-ens';
import { ethers } from 'ethers';
import ensNetworkMap from 'ethereum-ens-network-map';
import { NETWORK_ID_TO_ETHERS_NETWORK_NAME_MAP } from '../../../../shared/constants/network';
export default class Ens {
static getNetworkEnsSupport(network) {
@ -7,17 +8,21 @@ export default class Ens {
}
constructor({ network, provider } = {}) {
this._ethJsEns = new EthJsEns({
network,
provider,
const networkName = NETWORK_ID_TO_ETHERS_NETWORK_NAME_MAP[network];
const ensAddress = ensNetworkMap[network];
const ethProvider = new ethers.providers.Web3Provider(provider, {
chainId: parseInt(network, 10),
name: networkName,
ensAddress,
});
this._ethProvider = ethProvider;
}
lookup(ensName) {
return this._ethJsEns.lookup(ensName);
return this._ethProvider.resolveName(ensName);
}
reverse(address) {
return this._ethJsEns.reverse(address);
return this._ethProvider.lookupAddress(address);
}
}

@ -20,7 +20,7 @@ describe('EnsController', function () {
describe('#constructor', function () {
it('should construct the controller given a provider and a network', async function () {
const ens = new EnsController({
provider: {},
provider: sinon.fake(),
getCurrentChainId,
onNetworkDidChange,
});

@ -17,10 +17,10 @@ import {
MAINNET_CHAIN_ID,
RINKEBY_CHAIN_ID,
ROPSTEN_CHAIN_ID,
SEPOLIA_CHAIN_ID,
} from '../../../shared/constants/network';
import { SECOND } from '../../../shared/constants/time';
const fetchWithTimeout = getFetchWithTimeout(SECOND * 30);
const fetchWithTimeout = getFetchWithTimeout();
/**
* @typedef {import('../../../shared/constants/transaction').TransactionMeta} TransactionMeta
@ -59,6 +59,7 @@ const etherscanSupportedNetworks = [
MAINNET_CHAIN_ID,
RINKEBY_CHAIN_ID,
ROPSTEN_CHAIN_ID,
SEPOLIA_CHAIN_ID,
];
export default class IncomingTransactionsController {
@ -87,6 +88,7 @@ export default class IncomingTransactionsController {
[MAINNET_CHAIN_ID]: null,
[RINKEBY_CHAIN_ID]: null,
[ROPSTEN_CHAIN_ID]: null,
[SEPOLIA_CHAIN_ID]: null,
},
...opts.initState,
};

@ -12,6 +12,7 @@ import {
MAINNET_CHAIN_ID,
RINKEBY_CHAIN_ID,
ROPSTEN_CHAIN_ID,
SEPOLIA_CHAIN_ID,
ROPSTEN_NETWORK_ID,
ROPSTEN,
} from '../../../shared/constants/network';
@ -39,6 +40,7 @@ const PREPOPULATED_BLOCKS_BY_NETWORK = {
[MAINNET_CHAIN_ID]: 3,
[RINKEBY_CHAIN_ID]: 5,
[ROPSTEN_CHAIN_ID]: 4,
[SEPOLIA_CHAIN_ID]: 6,
};
const EMPTY_BLOCKS_BY_NETWORK = {
[GOERLI_CHAIN_ID]: null,
@ -46,6 +48,7 @@ const EMPTY_BLOCKS_BY_NETWORK = {
[MAINNET_CHAIN_ID]: null,
[RINKEBY_CHAIN_ID]: null,
[ROPSTEN_CHAIN_ID]: null,
[SEPOLIA_CHAIN_ID]: null,
};
function getEmptyInitState() {
@ -153,6 +156,7 @@ function nockEtherscanApiForAllChains(mockResponse) {
MAINNET_CHAIN_ID,
RINKEBY_CHAIN_ID,
ROPSTEN_CHAIN_ID,
SEPOLIA_CHAIN_ID,
'undefined',
]) {
nock(
@ -190,9 +194,8 @@ describe('IncomingTransactionsController', function () {
);
assert(mockedNetworkMethods.onNetworkDidChange.calledOnce);
const networkControllerListenerCallback = mockedNetworkMethods.onNetworkDidChange.getCall(
0,
).args[0];
const networkControllerListenerCallback =
mockedNetworkMethods.onNetworkDidChange.getCall(0).args[0];
assert.strictEqual(incomingTransactionsController._update.callCount, 0);
networkControllerListenerCallback('testNetworkType');
assert.strictEqual(incomingTransactionsController._update.callCount, 1);
@ -253,8 +256,10 @@ describe('IncomingTransactionsController', function () {
initState: getNonEmptyInitState(),
},
);
const startBlock = getNonEmptyInitState()
.incomingTxLastFetchedBlockByChainId[ROPSTEN_CHAIN_ID];
const startBlock =
getNonEmptyInitState().incomingTxLastFetchedBlockByChainId[
ROPSTEN_CHAIN_ID
];
nock('https://api-ropsten.etherscan.io')
.get(
`/api?module=account&action=txlist&address=${MOCK_SELECTED_ADDRESS}&tag=latest&page=1&startBlock=${startBlock}`,
@ -546,8 +551,10 @@ describe('IncomingTransactionsController', function () {
},
);
const NEW_MOCK_SELECTED_ADDRESS = `${MOCK_SELECTED_ADDRESS}9`;
const startBlock = getNonEmptyInitState()
.incomingTxLastFetchedBlockByChainId[ROPSTEN_CHAIN_ID];
const startBlock =
getNonEmptyInitState().incomingTxLastFetchedBlockByChainId[
ROPSTEN_CHAIN_ID
];
nock('https://api-ropsten.etherscan.io')
.get(
`/api?module=account&action=txlist&address=${NEW_MOCK_SELECTED_ADDRESS}&tag=latest&page=1&startBlock=${startBlock}`,
@ -572,7 +579,8 @@ describe('IncomingTransactionsController', function () {
incomingTransactionsController.store,
);
const subscription = incomingTransactionsController.preferencesController.store.subscribe.getCall(
const subscription =
incomingTransactionsController.preferencesController.store.subscribe.getCall(
1,
).args[0];
// The incoming transactions controller will always skip the first event
@ -658,7 +666,8 @@ describe('IncomingTransactionsController', function () {
incomingTransactionsController.store,
);
const subscription = incomingTransactionsController.preferencesController.store.subscribe.getCall(
const subscription =
incomingTransactionsController.preferencesController.store.subscribe.getCall(
1,
).args[0];
// The incoming transactions controller will always skip the first event
@ -682,9 +691,8 @@ describe('IncomingTransactionsController', function () {
});
it('should update when switching to a supported network', async function () {
const mockedNetworkMethods = getMockNetworkControllerMethods(
ROPSTEN_CHAIN_ID,
);
const mockedNetworkMethods =
getMockNetworkControllerMethods(ROPSTEN_CHAIN_ID);
const incomingTransactionsController = new IncomingTransactionsController(
{
blockTracker: getMockBlockTracker(),
@ -693,8 +701,10 @@ describe('IncomingTransactionsController', function () {
initState: getNonEmptyInitState(),
},
);
const startBlock = getNonEmptyInitState()
.incomingTxLastFetchedBlockByChainId[ROPSTEN_CHAIN_ID];
const startBlock =
getNonEmptyInitState().incomingTxLastFetchedBlockByChainId[
ROPSTEN_CHAIN_ID
];
nock('https://api-ropsten.etherscan.io')
.get(
`/api?module=account&action=txlist&address=${MOCK_SELECTED_ADDRESS}&tag=latest&page=1&startBlock=${startBlock}`,
@ -715,8 +725,8 @@ describe('IncomingTransactionsController', function () {
incomingTransactionsController.store,
);
const subscription = mockedNetworkMethods.onNetworkDidChange.getCall(0)
.args[0];
const subscription =
mockedNetworkMethods.onNetworkDidChange.getCall(0).args[0];
await subscription(ROPSTEN_CHAIN_ID);
await updateStateCalled();
@ -763,9 +773,8 @@ describe('IncomingTransactionsController', function () {
});
it('should not update when switching to an unsupported network', async function () {
const mockedNetworkMethods = getMockNetworkControllerMethods(
ROPSTEN_CHAIN_ID,
);
const mockedNetworkMethods =
getMockNetworkControllerMethods(ROPSTEN_CHAIN_ID);
const incomingTransactionsController = new IncomingTransactionsController(
{
blockTracker: getMockBlockTracker(),
@ -796,8 +805,8 @@ describe('IncomingTransactionsController', function () {
incomingTransactionsController.store,
);
const subscription = mockedNetworkMethods.onNetworkDidChange.getCall(0)
.args[0];
const subscription =
mockedNetworkMethods.onNetworkDidChange.getCall(0).args[0];
incomingTransactionsController.getCurrentChainId = () => FAKE_CHAIN_ID;
await subscription();
@ -820,15 +829,14 @@ describe('IncomingTransactionsController', function () {
describe('_update', function () {
describe('when state is empty (initialized)', function () {
it('should use provided block number and update the latest block seen', async function () {
const incomingTransactionsController = new IncomingTransactionsController(
{
const incomingTransactionsController =
new IncomingTransactionsController({
blockTracker: getMockBlockTracker(),
...getMockNetworkControllerMethods(ROPSTEN_CHAIN_ID),
preferencesController: getMockPreferencesController(),
initState: getEmptyInitState(),
getCurrentChainId: () => ROPSTEN_CHAIN_ID,
},
);
});
sinon.spy(incomingTransactionsController.store, 'updateState');
incomingTransactionsController._getNewIncomingTransactions = sinon
@ -857,15 +865,14 @@ describe('IncomingTransactionsController', function () {
});
it('should update the last fetched block for network to highest block seen in incoming txs', async function () {
const incomingTransactionsController = new IncomingTransactionsController(
{
const incomingTransactionsController =
new IncomingTransactionsController({
blockTracker: getMockBlockTracker(),
...getMockNetworkControllerMethods(ROPSTEN_CHAIN_ID),
preferencesController: getMockPreferencesController(),
initState: getEmptyInitState(),
getCurrentChainId: () => ROPSTEN_CHAIN_ID,
},
);
});
const NEW_TRANSACTION_ONE = {
id: 555,
@ -911,15 +918,14 @@ describe('IncomingTransactionsController', function () {
describe('when state is populated with prior data for network', function () {
it('should use the last fetched block for the current network and increment by 1 in state', async function () {
const incomingTransactionsController = new IncomingTransactionsController(
{
const incomingTransactionsController =
new IncomingTransactionsController({
blockTracker: getMockBlockTracker(),
...getMockNetworkControllerMethods(ROPSTEN_CHAIN_ID),
preferencesController: getMockPreferencesController(),
initState: getNonEmptyInitState(),
getCurrentChainId: () => ROPSTEN_CHAIN_ID,
},
);
});
sinon.spy(incomingTransactionsController.store, 'updateState');
incomingTransactionsController._getNewIncomingTransactions = sinon
.stub()
@ -1105,7 +1111,8 @@ describe('IncomingTransactionsController', function () {
},
);
const result = await incomingTransactionsController._getNewIncomingTransactions(
const result =
await incomingTransactionsController._getNewIncomingTransactions(
ADDRESS_TO_FETCH_FOR,
'789',
ROPSTEN_CHAIN_ID,
@ -1137,7 +1144,8 @@ describe('IncomingTransactionsController', function () {
},
);
const result = await incomingTransactionsController._getNewIncomingTransactions(
const result =
await incomingTransactionsController._getNewIncomingTransactions(
ADDRESS_TO_FETCH_FOR,
'789',
ROPSTEN_CHAIN_ID,
@ -1164,7 +1172,8 @@ describe('IncomingTransactionsController', function () {
},
);
const result = await incomingTransactionsController._getNewIncomingTransactions(
const result =
await incomingTransactionsController._getNewIncomingTransactions(
ADDRESS_TO_FETCH_FOR,
'789',
ROPSTEN_CHAIN_ID,

@ -10,7 +10,7 @@ import {
} from 'lodash';
import { ObservableStore } from '@metamask/obs-store';
import { bufferToHex, keccak } from 'ethereumjs-util';
import { generateUUID } from 'pubnub';
import { v4 as uuidv4 } from 'uuid';
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app';
import {
METAMETRICS_ANONYMOUS_ID,
@ -19,6 +19,8 @@ import {
} from '../../../shared/constants/metametrics';
import { SECOND } from '../../../shared/constants/time';
const EXTENSION_UNINSTALL_URL = 'https://metamask.io/uninstalled';
const defaultCaptureException = (err) => {
// throw error on clean stack so its captured by platform integrations (eg sentry)
// but does not interrupt the call stack
@ -52,6 +54,9 @@ const exceptionsToFilter = {
* whether or not events are tracked
* @property {{[string]: MetaMetricsEventFragment}} [fragments] - Object keyed
* by UUID with stored fragments as values.
* @property {Array} [eventsBeforeMetricsOptIn] - Array of queued events added before
* a user opts into metrics.
* @property {object} [traits] - Traits that are not derived from other state keys.
*/
export default class MetaMetricsController {
@ -69,6 +74,7 @@ export default class MetaMetricsController {
* identifier from the network controller
* @param {string} options.version - The version of the extension
* @param {string} options.environment - The environment the extension is running in
* @param {string} options.extension - webextension-polyfill
* @param {MetaMetricsControllerState} options.initState - State to initialized with
* @param options.captureException
*/
@ -81,6 +87,7 @@ export default class MetaMetricsController {
version,
environment,
initState,
extension,
captureException = defaultCaptureException,
}) {
this._captureException = (err) => {
@ -96,12 +103,16 @@ export default class MetaMetricsController {
this.locale = prefState.currentLocale.replace('_', '-');
this.version =
environment === 'production' ? version : `${version}-${environment}`;
this.extension = extension;
this.environment = environment;
const abandonedFragments = omitBy(initState?.fragments, 'persist');
this.store = new ObservableStore({
participateInMetaMetrics: null,
metaMetricsId: null,
eventsBeforeMetricsOptIn: [],
traits: {},
...initState,
fragments: {
...initState?.fragments,
@ -179,7 +190,7 @@ export default class MetaMetricsController {
}
const { fragments } = this.store.getState();
const id = options.uniqueIdentifier ?? generateUUID();
const id = options.uniqueIdentifier ?? uuidv4();
const fragment = {
id,
...options,
@ -315,6 +326,26 @@ export default class MetaMetricsController {
this._identify(allValidTraits);
}
// It sets an uninstall URL ("Sorry to see you go!" page),
// which is opened if a user uninstalls the extension.
updateExtensionUninstallUrl(participateInMetaMetrics, metaMetricsId) {
const query = {};
if (participateInMetaMetrics) {
// We only want to track these things if a user opted into metrics.
query.mmi = Buffer.from(metaMetricsId).toString('base64');
query.env = this.environment;
query.av = this.version;
}
const queryString = new URLSearchParams(query);
// this.extension not currently defined in tests
if (this.extension && this.extension.runtime) {
this.extension.runtime.setUninstallURL(
`${EXTENSION_UNINSTALL_URL}?${queryString}`,
);
}
}
/**
* Setter for the `participateInMetaMetrics` property
*
@ -331,6 +362,12 @@ export default class MetaMetricsController {
metaMetricsId = null;
}
this.store.updateState({ participateInMetaMetrics, metaMetricsId });
if (participateInMetaMetrics) {
this.trackEventsAfterMetricsOptIn();
this.clearEventsAfterMetricsOptIn();
}
this.updateExtensionUninstallUrl(participateInMetaMetrics, metaMetricsId);
return metaMetricsId;
}
@ -472,6 +509,37 @@ export default class MetaMetricsController {
}
}
// Track all queued events after a user opted into metrics.
trackEventsAfterMetricsOptIn() {
const { eventsBeforeMetricsOptIn } = this.store.getState();
eventsBeforeMetricsOptIn.forEach((eventBeforeMetricsOptIn) => {
this.trackEvent(eventBeforeMetricsOptIn);
});
}
// Once we track queued events after a user opts into metrics, we want to clear the event queue.
clearEventsAfterMetricsOptIn() {
this.store.updateState({
eventsBeforeMetricsOptIn: [],
});
}
// It adds an event into a queue, which is only tracked if a user opts into metrics.
addEventBeforeMetricsOptIn(event) {
const prevState = this.store.getState().eventsBeforeMetricsOptIn;
this.store.updateState({
eventsBeforeMetricsOptIn: [...prevState, event],
});
}
// Add or update traits for tracking.
updateTraits(newTraits) {
const { traits } = this.store.getState();
this.store.updateState({
traits: { ...traits, ...newTraits },
});
}
/** PRIVATE METHODS */
/**
@ -549,16 +617,19 @@ export default class MetaMetricsController {
* @returns {MetaMetricsTraits | null} traits that have changed since last update
*/
_buildUserTraitsObject(metamaskState) {
const { traits } = this.store.getState();
/** @type {MetaMetricsTraits} */
const currentTraits = {
[TRAITS.ADDRESS_BOOK_ENTRIES]: sum(
Object.values(metamaskState.addressBook).map(size),
),
[TRAITS.INSTALL_DATE_EXT]: traits[TRAITS.INSTALL_DATE_EXT] || '',
[TRAITS.LEDGER_CONNECTION_TYPE]: metamaskState.ledgerTransportType,
[TRAITS.NETWORKS_ADDED]: metamaskState.frequentRpcListDetail.map(
(rpc) => rpc.chainId,
),
[TRAITS.NETWORKS_WITHOUT_TICKER]: metamaskState.frequentRpcListDetail.reduce(
[TRAITS.NETWORKS_WITHOUT_TICKER]:
metamaskState.frequentRpcListDetail.reduce(
(networkList, currentNetwork) => {
if (!currentNetwork.ticker) {
networkList.push(currentNetwork.chainId);

@ -132,12 +132,10 @@ function getMetaMetricsController({
} = {}) {
return new MetaMetricsController({
segment,
getNetworkIdentifier: networkController.getNetworkIdentifier.bind(
networkController,
),
getCurrentChainId: networkController.getCurrentChainId.bind(
networkController,
),
getNetworkIdentifier:
networkController.getNetworkIdentifier.bind(networkController),
getCurrentChainId:
networkController.getCurrentChainId.bind(networkController),
onNetworkDidChange: networkController.on.bind(
networkController,
NETWORK_EVENTS.NETWORK_DID_CHANGE,
@ -690,6 +688,7 @@ describe('MetaMetricsController', function () {
assert.deepEqual(traits, {
[TRAITS.ADDRESS_BOOK_ENTRIES]: 3,
[TRAITS.INSTALL_DATE_EXT]: '',
[TRAITS.LEDGER_CONNECTION_TYPE]: 'web-hid',
[TRAITS.NETWORKS_ADDED]: [MAINNET_CHAIN_ID, ROPSTEN_CHAIN_ID, '0xaf'],
[TRAITS.NETWORKS_WITHOUT_TICKER]: ['0xaf'],

@ -34,8 +34,8 @@ describe('NetworkController', () => {
describe('#provider', () => {
it('provider should be updatable without reassignment', () => {
networkController.initializeProvider(networkControllerProviderConfig);
const providerProxy = networkController.getProviderAndBlockTracker()
.provider;
const providerProxy =
networkController.getProviderAndBlockTracker().provider;
expect(providerProxy.test).toBeUndefined();
providerProxy.setTarget({ test: true });
expect(providerProxy.test).toStrictEqual(true);
@ -79,7 +79,8 @@ describe('NetworkController', () => {
describe('#getEIP1559Compatibility', () => {
it('should return false when baseFeePerGas is not in the block header', async () => {
networkController.initializeProvider(networkControllerProviderConfig);
const supportsEIP1559 = await networkController.getEIP1559Compatibility();
const supportsEIP1559 =
await networkController.getEIP1559Compatibility();
expect(supportsEIP1559).toStrictEqual(false);
});
@ -88,7 +89,8 @@ describe('NetworkController', () => {
getLatestBlockStub.callsFake(() =>
Promise.resolve({ baseFeePerGas: '0xa ' }),
);
const supportsEIP1559 = await networkController.getEIP1559Compatibility();
const supportsEIP1559 =
await networkController.getEIP1559Compatibility();
expect(supportsEIP1559).toStrictEqual(true);
});
@ -98,7 +100,8 @@ describe('NetworkController', () => {
Promise.resolve({ baseFeePerGas: '0xa ' }),
);
await networkController.getEIP1559Compatibility();
const supportsEIP1559 = await networkController.getEIP1559Compatibility();
const supportsEIP1559 =
await networkController.getEIP1559Compatibility();
expect(getLatestBlockStub.calledOnce).toStrictEqual(true);
expect(supportsEIP1559).toStrictEqual(true);
});
@ -174,6 +177,10 @@ describe('NetworkController', () => {
input: 'goerli',
expected: 'Goerli',
},
{
input: 'sepolia',
expected: 'Sepolia',
},
];
tests.forEach(({ input, expected }) =>

@ -20,7 +20,6 @@ import {
INFURA_BLOCKED_KEY,
TEST_NETWORK_TICKER_MAP,
} from '../../../../shared/constants/network';
import { SECOND } from '../../../../shared/constants/time';
import {
isPrefixedFormattedHexString,
isSafeChainId,
@ -31,7 +30,7 @@ import createInfuraClient from './createInfuraClient';
import createJsonRpcClient from './createJsonRpcClient';
const env = process.env.METAMASK_ENV;
const fetchWithTimeout = getFetchWithTimeout(SECOND * 30);
const fetchWithTimeout = getFetchWithTimeout();
let defaultProviderConfigOpts;
if (process.env.IN_TEST) {

@ -68,8 +68,7 @@ describe('PendingNonceMiddleware', () => {
from: '0xf231d46dd78806e1dd93442cf33c7671f8538748',
gas: GAS_LIMITS.SIMPLE,
gasPrice: '0x1e8480',
hash:
'0x2cc5a25744486f7383edebbf32003e5a66e18135799593d6b5cdd2bb43674f09',
hash: '0x2cc5a25744486f7383edebbf32003e5a66e18135799593d6b5cdd2bb43674f09',
input: '0x',
nonce: '0x4',
type: TRANSACTION_ENVELOPE_TYPES.LEGACY,

@ -25,8 +25,7 @@ describe('network utils', () => {
chainId: '0x3',
time: 1624408066355,
metamaskNetworkId: '3',
hash:
'0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7',
hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7',
r: '0x4c3111e42ed5eec3dcecba1e234700f387e8693c373c61c3e54a762a26f1570e',
s: '0x18bfc4eeb7ebcfacc3bd59ea100a6834ea3265e65945dbec69aa2a06564fafff',
v: '0x29',
@ -38,8 +37,7 @@ describe('network utils', () => {
from: '0xc684832530fcbddae4b4230a47e991ddcec2831d',
gas: '0x7b0d',
gasPrice: '0x77359400',
hash:
'0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7',
hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7',
input: '0x',
maxFeePerGas: '0x77359400',
maxPriorityFeePerGas: '0x77359400',
@ -72,8 +70,7 @@ describe('network utils', () => {
chainId: '0x3',
time: 1624408066355,
metamaskNetworkId: '3',
hash:
'0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7',
hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7',
r: '0x4c3111e42ed5eec3dcecba1e234700f387e8693c373c61c3e54a762a26f1570e',
s: '0x18bfc4eeb7ebcfacc3bd59ea100a6834ea3265e65945dbec69aa2a06564fafff',
v: '0x29',
@ -84,8 +81,7 @@ describe('network utils', () => {
blockNumber: null,
from: '0xc684832530fcbddae4b4230a47e991ddcec2831d',
gas: '0x7b0d',
hash:
'0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7',
hash: '0x4bcb6cd6b182209585f8ad140260ddb35c81a575dd40f508d9767e652a9f60e7',
input: '0x',
gasPrice: '0x77359400',
nonce: '0x4b',

@ -4,9 +4,8 @@ import { CaveatMutatorFactories } from './caveat-mutators';
describe('caveat mutators', () => {
describe('restrictReturnedAccounts', () => {
const { removeAccount } = CaveatMutatorFactories[
CaveatTypes.restrictReturnedAccounts
];
const { removeAccount } =
CaveatMutatorFactories[CaveatTypes.restrictReturnedAccounts];
describe('removeAccount', () => {
it('returns the no-op operation if the target account is not permitted', () => {

@ -31,7 +31,6 @@ describe('buildSnapRestrictedMethodSpecifications', () => {
expect(specification).toMatchObject({
targetKey: expect.stringMatching(/^(snap_|wallet_)/u),
methodImplementation: expect.any(Function),
allowedCaveats: null,
});
});
});

@ -1,4 +1,7 @@
import { constructPermission, PermissionType } from '@metamask/controllers';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import { caveatSpecifications as snapsCaveatsSpecifications } from '@metamask/rpc-methods';
///: END:ONLY_INCLUDE_IN
import {
CaveatTypes,
RestrictedMethods,
@ -63,6 +66,10 @@ export const getCaveatSpecifications = ({ getIdentities }) => {
validator: (caveat, _origin, _target) =>
validateCaveatAccounts(caveat.value, getIdentities),
},
///: BEGIN:ONLY_INCLUDE_IN(flask)
...snapsCaveatsSpecifications,
///: END:ONLY_INCLUDE_IN
};
};

@ -1,3 +1,4 @@
import { SnapCaveatType } from '@metamask/rpc-methods';
import {
CaveatTypes,
RestrictedMethods,
@ -15,10 +16,17 @@ describe('PermissionController specifications', () => {
describe('caveat specifications', () => {
it('getCaveatSpecifications returns the expected specifications object', () => {
const caveatSpecifications = getCaveatSpecifications({});
expect(Object.keys(caveatSpecifications)).toHaveLength(1);
expect(Object.keys(caveatSpecifications)).toHaveLength(3);
expect(
caveatSpecifications[CaveatTypes.restrictReturnedAccounts].type,
).toStrictEqual(CaveatTypes.restrictReturnedAccounts);
expect(caveatSpecifications.permittedDerivationPaths.type).toStrictEqual(
SnapCaveatType.PermittedDerivationPaths,
);
expect(caveatSpecifications.permittedCoinTypes.type).toStrictEqual(
SnapCaveatType.PermittedCoinTypes,
);
});
describe('restrictReturnedAccounts', () => {

@ -38,7 +38,7 @@ export default class PreferencesController {
// set to true means the dynamic list from the API is being used
// set to false will be using the static list from contract-metadata
useTokenDetection: Boolean(process.env.TOKEN_DETECTION_V2),
useTokenDetection: false,
useCollectibleDetection: false,
openSeaEnabled: false,
advancedGasFee: null,
@ -79,6 +79,7 @@ export default class PreferencesController {
this.store.setMaxListeners(12);
this.openPopup = opts.openPopup;
this.migrateAddressBookState = opts.migrateAddressBookState;
this.tokenListController = opts.tokenListController;
this._subscribeToInfuraAvailability();
@ -131,6 +132,13 @@ export default class PreferencesController {
*/
setUseTokenDetection(val) {
this.store.updateState({ useTokenDetection: val });
this.tokenListController.updatePreventPollingOnNetworkRestart(!val);
if (val) {
this.tokenListController.start();
} else {
this.tokenListController.clearingTokenListData();
this.tokenListController.stop();
}
}
/**

@ -1,5 +1,9 @@
import { strict as assert } from 'assert';
import sinon from 'sinon';
import {
ControllerMessenger,
TokenListController,
} from '@metamask/controllers';
import { MAINNET_CHAIN_ID } from '../../../shared/constants/network';
import PreferencesController from './preferences';
import NetworkController from './network';
@ -9,6 +13,7 @@ describe('preferences controller', function () {
let network;
let currentChainId;
let provider;
let tokenListController;
const migrateAddressBookState = sinon.stub();
beforeEach(function () {
@ -21,6 +26,16 @@ describe('preferences controller', function () {
network.setInfuraProjectId('foo');
network.initializeProvider(networkControllerProviderConfig);
provider = network.getProviderAndBlockTracker().provider;
const tokenListMessenger = new ControllerMessenger().getRestricted({
name: 'TokenListController',
});
tokenListController = new TokenListController({
chainId: '1',
preventPollingOnNetworkRestart: false,
onNetworkStateChange: sinon.spy(),
onPreferencesStateChange: sinon.spy(),
messenger: tokenListMessenger,
});
sandbox
.stub(network, 'getLatestBlock')
@ -35,6 +50,7 @@ describe('preferences controller', function () {
migrateAddressBookState,
network,
provider,
tokenListController,
});
});

@ -90,7 +90,8 @@ const initialState = {
swapsQuoteRefreshTime: FALLBACK_QUOTE_REFRESH_TIME,
swapsQuotePrefetchingRefreshTime: FALLBACK_QUOTE_REFRESH_TIME,
swapsStxBatchStatusRefreshTime: FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME,
swapsStxGetTransactionsRefreshTime: FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME,
swapsStxGetTransactionsRefreshTime:
FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME,
swapsStxMaxFeeMultiplier: FALLBACK_SMART_TRANSACTIONS_MAX_FEE_MULTIPLIER,
swapsFeatureFlags: {},
},
@ -336,10 +337,8 @@ export default class SwapsController {
if (Object.values(newQuotes).length === 0) {
this.setSwapsErrorKey(QUOTES_NOT_AVAILABLE_ERROR);
} else {
const [
_topAggId,
quotesWithSavingsAndFeeData,
] = await this._findTopQuoteAndCalculateSavings(newQuotes);
const [_topAggId, quotesWithSavingsAndFeeData] =
await this._findTopQuoteAndCalculateSavings(newQuotes);
topAggId = _topAggId;
newQuotes = quotesWithSavingsAndFeeData;
}
@ -486,10 +485,8 @@ export default class SwapsController {
const quoteToUpdate = { ...swapsState.quotes[initialAggId] };
const {
gasLimit: newGasEstimate,
simulationFails,
} = await this.timedoutGasReturn(quoteToUpdate.trade);
const { gasLimit: newGasEstimate, simulationFails } =
await this.timedoutGasReturn(quoteToUpdate.trade);
if (newGasEstimate && !simulationFails) {
const gasEstimateWithRefund = calculateGasEstimateWithRefund(
@ -637,9 +634,8 @@ export default class SwapsController {
}
async _findTopQuoteAndCalculateSavings(quotes = {}) {
const {
contractExchangeRates: tokenConversionRates,
} = this.getTokenRatesState();
const { contractExchangeRates: tokenConversionRates } =
this.getTokenRatesState();
const {
swapsState: { customGasPrice, customMaxPriorityFeePerGas },
} = this.store.getState();
@ -652,10 +648,8 @@ export default class SwapsController {
const newQuotes = cloneDeep(quotes);
const {
gasFeeEstimates,
gasEstimateType,
} = await this._getEIP1559GasFeeEstimates();
const { gasFeeEstimates, gasEstimateType } =
await this._getEIP1559GasFeeEstimates();
let usedGasPrice = '0x0';
@ -756,9 +750,8 @@ export default class SwapsController {
const tokenPercentageOfPreFeeDestAmount = new BigNumber(100, 10)
.minus(metaMaskFee, 10)
.div(100);
const destinationAmountBeforeMetaMaskFee = decimalAdjustedDestinationAmount.div(
tokenPercentageOfPreFeeDestAmount,
);
const destinationAmountBeforeMetaMaskFee =
decimalAdjustedDestinationAmount.div(tokenPercentageOfPreFeeDestAmount);
const metaMaskFeeInTokens = destinationAmountBeforeMetaMaskFee.minus(
decimalAdjustedDestinationAmount,
);

@ -42,8 +42,7 @@ const TEST_AGG_ID_APPROVAL = 'TEST_AGG_APPROVAL';
const POLLING_TIMEOUT = SECOND * 1000;
const MOCK_APPROVAL_NEEDED = {
data:
'0x095ea7b300000000000000000000000095e6f48254609a6ee006f7d493c8e5fb97094cef0000000000000000000000000000000000000000004a817c7ffffffdabf41c00',
data: '0x095ea7b300000000000000000000000095e6f48254609a6ee006f7d493c8e5fb97094cef0000000000000000000000000000000000000000004a817c7ffffffdabf41c00',
to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
amount: '0',
from: '0x2369267687A84ac7B494daE2f1542C40E37f4455',
@ -139,7 +138,8 @@ const EMPTY_INIT_STATE = {
swapsQuoteRefreshTime: 60000,
swapsQuotePrefetchingRefreshTime: 60000,
swapsStxBatchStatusRefreshTime: FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME,
swapsStxGetTransactionsRefreshTime: FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME,
swapsStxGetTransactionsRefreshTime:
FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME,
swapsStxMaxFeeMultiplier: FALLBACK_SMART_TRANSACTIONS_MAX_FEE_MULTIPLIER,
swapsUserFeeLevel: '',
saveFetchedQuotes: false,
@ -370,13 +370,10 @@ describe('SwapsController', function () {
baseGasEstimate,
);
const {
gasLimit: bufferedGasLimit,
} = await swapsController.getBufferedGasLimit();
const {
gasEstimate,
gasEstimateWithRefund,
} = swapsController.store.getState().swapsState.quotes[initialAggId];
const { gasLimit: bufferedGasLimit } =
await swapsController.getBufferedGasLimit();
const { gasEstimate, gasEstimateWithRefund } =
swapsController.store.getState().swapsState.quotes[initialAggId];
assert.strictEqual(gasEstimate, bufferedGasLimit);
assert.strictEqual(
gasEstimateWithRefund,
@ -416,10 +413,8 @@ describe('SwapsController', function () {
});
it('returns the top aggId and quotes with savings and fee values if passed necessary data and an even number of quotes', async function () {
const [
topAggId,
resultQuotes,
] = await swapsController._findTopQuoteAndCalculateSavings(
const [topAggId, resultQuotes] =
await swapsController._findTopQuoteAndCalculateSavings(
getTopQuoteAndSavingsMockQuotes(),
);
assert.equal(topAggId, TEST_AGG_ID_1);
@ -442,10 +437,8 @@ describe('SwapsController', function () {
medianMetaMaskFee: '0.0202',
};
const [
topAggId,
resultQuotes,
] = await swapsController._findTopQuoteAndCalculateSavings(testInput);
const [topAggId, resultQuotes] =
await swapsController._findTopQuoteAndCalculateSavings(testInput);
assert.equal(topAggId, TEST_AGG_ID_1);
assert.deepStrictEqual(resultQuotes, expectedResultQuotes);
});
@ -485,10 +478,8 @@ describe('SwapsController', function () {
},
};
const [
topAggId,
resultQuotes,
] = await swapsController._findTopQuoteAndCalculateSavings(testInput);
const [topAggId, resultQuotes] =
await swapsController._findTopQuoteAndCalculateSavings(testInput);
assert.equal(topAggId, TEST_AGG_ID_1);
assert.deepStrictEqual(resultQuotes, expectedResultQuotes);
});
@ -503,7 +494,8 @@ describe('SwapsController', function () {
trade: { value: '0x8ac7230489e80000' },
}),
);
const baseExpectedResultQuotes = getTopQuoteAndSavingsBaseExpectedResults();
const baseExpectedResultQuotes =
getTopQuoteAndSavingsBaseExpectedResults();
const expectedResultQuotes = {
[TEST_AGG_ID_1]: {
...baseExpectedResultQuotes[TEST_AGG_ID_1],
@ -549,10 +541,8 @@ describe('SwapsController', function () {
},
};
const [
topAggId,
resultQuotes,
] = await swapsController._findTopQuoteAndCalculateSavings(testInput);
const [topAggId, resultQuotes] =
await swapsController._findTopQuoteAndCalculateSavings(testInput);
assert.equal(topAggId, TEST_AGG_ID_1);
assert.deepStrictEqual(resultQuotes, expectedResultQuotes);
});
@ -569,7 +559,8 @@ describe('SwapsController', function () {
);
// 0.04 ETH fee included in trade value
testInput[TEST_AGG_ID_1].trade.value = '0x8b553ece48ec0000';
const baseExpectedResultQuotes = getTopQuoteAndSavingsBaseExpectedResults();
const baseExpectedResultQuotes =
getTopQuoteAndSavingsBaseExpectedResults();
const expectedResultQuotes = {
[TEST_AGG_ID_1]: {
...baseExpectedResultQuotes[TEST_AGG_ID_1],
@ -626,10 +617,8 @@ describe('SwapsController', function () {
delete expectedResultQuotes[TEST_AGG_ID_1].isBestQuote;
delete expectedResultQuotes[TEST_AGG_ID_1].savings;
const [
topAggId,
resultQuotes,
] = await swapsController._findTopQuoteAndCalculateSavings(testInput);
const [topAggId, resultQuotes] =
await swapsController._findTopQuoteAndCalculateSavings(testInput);
assert.equal(topAggId, TEST_AGG_ID_2);
assert.deepStrictEqual(resultQuotes, expectedResultQuotes);
});
@ -638,7 +627,8 @@ describe('SwapsController', function () {
const testInput = getTopQuoteAndSavingsMockQuotes();
// 0.04 ETH fee included in trade value
testInput[TEST_AGG_ID_1].trade.value = '0x8e1bc9bf040000';
const baseExpectedResultQuotes = getTopQuoteAndSavingsBaseExpectedResults();
const baseExpectedResultQuotes =
getTopQuoteAndSavingsBaseExpectedResults();
const expectedResultQuotes = {
...baseExpectedResultQuotes,
[TEST_AGG_ID_1]: {
@ -662,10 +652,8 @@ describe('SwapsController', function () {
delete expectedResultQuotes[TEST_AGG_ID_1].isBestQuote;
delete expectedResultQuotes[TEST_AGG_ID_1].savings;
const [
topAggId,
resultQuotes,
] = await swapsController._findTopQuoteAndCalculateSavings(testInput);
const [topAggId, resultQuotes] =
await swapsController._findTopQuoteAndCalculateSavings(testInput);
assert.equal(topAggId, TEST_AGG_ID_2);
assert.deepStrictEqual(resultQuotes, expectedResultQuotes);
});

@ -225,9 +225,8 @@ export default class ThreeBoxController {
PreferencesController: preferences,
AddressBookController: addressBook,
};
const initialMigrationState = migrator.generateInitialState(
formattedStateBackup,
);
const initialMigrationState =
migrator.generateInitialState(formattedStateBackup);
const migratedState = await migrator.migrateData(initialMigrationState);
return {
preferences: migratedState.data.PreferencesController,

@ -27,6 +27,8 @@ import {
import {
TRANSACTION_STATUSES,
TRANSACTION_TYPES,
TRANSACTION_APPROVAL_AMOUNT_TYPE,
TOKEN_STANDARDS,
TRANSACTION_ENVELOPE_TYPES,
TRANSACTION_EVENTS,
} from '../../../../shared/constants/transaction';
@ -45,11 +47,13 @@ import { EVENT } from '../../../../shared/constants/metametrics';
import {
HARDFORKS,
MAINNET,
SEPOLIA,
NETWORK_TYPE_RPC,
CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP,
} from '../../../../shared/constants/network';
import {
determineTransactionAssetType,
determineTransactionContractCode,
determineTransactionType,
isEIP1559Transaction,
} from '../../../../shared/modules/transaction.utils';
@ -174,9 +178,8 @@ export default class TransactionController extends EventEmitter {
);
return [...pendingTransactions, ...externalPendingTransactions];
},
getConfirmedTransactions: this.txStateManager.getConfirmedTransactions.bind(
this.txStateManager,
),
getConfirmedTransactions:
this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
});
this.pendingTxTracker = new PendingTransactionTracker({
@ -189,9 +192,8 @@ export default class TransactionController extends EventEmitter {
return [...pending, ...approved];
},
approveTransaction: this.approveTransaction.bind(this),
getCompletedTransactions: this.txStateManager.getConfirmedTransactions.bind(
this.txStateManager,
),
getCompletedTransactions:
this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
});
this.txStateManager.store.subscribe(() =>
@ -227,10 +229,10 @@ export default class TransactionController extends EventEmitter {
}
async getEIP1559Compatibility(fromAddress) {
const currentNetworkIsCompatible = await this._getCurrentNetworkEIP1559Compatibility();
const fromAccountIsCompatible = await this._getCurrentAccountEIP1559Compatibility(
fromAddress,
);
const currentNetworkIsCompatible =
await this._getCurrentNetworkEIP1559Compatibility();
const fromAccountIsCompatible =
await this._getCurrentAccountEIP1559Compatibility(fromAddress);
return currentNetworkIsCompatible && fromAccountIsCompatible;
}
@ -256,7 +258,7 @@ export default class TransactionController extends EventEmitter {
// type will be one of our default network names or 'rpc'. the default
// network names are sufficient configuration, simply pass the name as the
// chain argument in the constructor.
if (type !== NETWORK_TYPE_RPC) {
if (type !== NETWORK_TYPE_RPC && type !== SEPOLIA) {
return new Common({
chain: type,
hardfork,
@ -672,14 +674,22 @@ export default class TransactionController extends EventEmitter {
* state is unapproved. Returns the updated transaction.
*
* @param {string} txId - transaction id
* @param {number} currentSendFlowHistoryLength - sendFlowHistory entries currently
* @param {Array<{ entry: string, timestamp: number }>} sendFlowHistory -
* history to add to the sendFlowHistory property of txMeta.
* @returns {TransactionMeta} the txMeta of the updated transaction
*/
updateTransactionSendFlowHistory(txId, sendFlowHistory) {
updateTransactionSendFlowHistory(
txId,
currentSendFlowHistoryLength,
sendFlowHistory,
) {
this._throwErrorIfNotUnapprovedTx(txId, 'updateTransactionSendFlowHistory');
const txMeta = this._getTransaction(txId);
if (
currentSendFlowHistoryLength === (txMeta?.sendFlowHistory?.length || 0)
) {
// only update what is defined
const note = `Update sendFlowHistory for ${txId}`;
@ -693,19 +703,58 @@ export default class TransactionController extends EventEmitter {
},
note,
);
}
return this._getTransaction(txId);
}
async addTransactionGasDefaults(txMeta) {
const contractCode = await determineTransactionContractCode(
txMeta.txParams,
this.query,
);
let updateTxMeta = txMeta;
try {
updateTxMeta = await this.addTxGasDefaults(txMeta, contractCode);
} catch (error) {
log.warn(error);
updateTxMeta = this.txStateManager.getTransaction(txMeta.id);
updateTxMeta.loadingDefaults = false;
this.txStateManager.updateTransaction(
txMeta,
'Failed to calculate gas defaults.',
);
throw error;
}
updateTxMeta.loadingDefaults = false;
// The history note used here 'Added new unapproved transaction.' is confusing update call only updated the gas defaults.
// We need to improve `this.addTransaction` to accept history note and change note here.
this.txStateManager.updateTransaction(
updateTxMeta,
'Added new unapproved transaction.',
);
return updateTxMeta;
}
// ====================================================================================================================================================
/**
* Validates and generates a txMeta with defaults and puts it in txStateManager
* store.
*
* actionId is used to uniquely identify a request to create a transaction.
* Only 1 transaction will be created for multiple requests with same actionId.
* actionId is fix used for making this action idempotent to deal with scenario when
* action is invoked multiple times with same parameters in MV3 due to service worker re-activation.
*
* @param txParams
* @param origin
* @param transactionType
* @param sendFlowHistory
* @param actionId
* @returns {txMeta}
*/
async addUnapprovedTransaction(
@ -713,6 +762,7 @@ export default class TransactionController extends EventEmitter {
origin,
transactionType,
sendFlowHistory = [],
actionId,
) {
if (
transactionType !== undefined &&
@ -723,6 +773,17 @@ export default class TransactionController extends EventEmitter {
);
}
// In transaction is found for same action id, do not create a new transaction.
if (actionId) {
let existingTxMeta =
this.txStateManager.getTransactionWithActionId(actionId);
if (existingTxMeta) {
this.emit('newUnapprovedTx', existingTxMeta);
existingTxMeta = await this.addTransactionGasDefaults(existingTxMeta);
return existingTxMeta;
}
}
// validate
const normalizedTxParams = txUtils.normalizeTxParams(txParams);
const eip1559Compatibility = await this.getEIP1559Compatibility();
@ -741,6 +802,12 @@ export default class TransactionController extends EventEmitter {
sendFlowHistory,
});
// Add actionId to txMeta to check if same actionId is seen again
// IF request to create transaction with same actionId is submitted again, new transaction will not be added for it.
if (actionId) {
txMeta.actionId = actionId;
}
if (origin === ORIGIN_METAMASK) {
// Assert the from address is the selected address
if (normalizedTxParams.from !== this.getSelectedAddress()) {
@ -762,10 +829,7 @@ export default class TransactionController extends EventEmitter {
}
}
const { type, getCodeResponse } = await determineTransactionType(
txParams,
this.query,
);
const { type } = await determineTransactionType(txParams, this.query);
txMeta.type = transactionType || type;
// ensure value
@ -776,25 +840,7 @@ export default class TransactionController extends EventEmitter {
this.addTransaction(txMeta);
this.emit('newUnapprovedTx', txMeta);
try {
txMeta = await this.addTxGasDefaults(txMeta, getCodeResponse);
} catch (error) {
log.warn(error);
txMeta = this.txStateManager.getTransaction(txMeta.id);
txMeta.loadingDefaults = false;
this.txStateManager.updateTransaction(
txMeta,
'Failed to calculate gas defaults.',
);
throw error;
}
txMeta.loadingDefaults = false;
// save txMeta
this.txStateManager.updateTransaction(
txMeta,
'Added new unapproved transaction.',
);
txMeta = await this.addTransactionGasDefaults(txMeta);
return txMeta;
}
@ -815,10 +861,8 @@ export default class TransactionController extends EventEmitter {
maxFeePerGas: defaultMaxFeePerGas,
maxPriorityFeePerGas: defaultMaxPriorityFeePerGas,
} = await this._getDefaultGasFees(txMeta, eip1559Compatibility);
const {
gasLimit: defaultGasLimit,
simulationFails,
} = await this._getDefaultGasLimit(txMeta, getCodeResponse);
const { gasLimit: defaultGasLimit, simulationFails } =
await this._getDefaultGasLimit(txMeta, getCodeResponse);
// eslint-disable-next-line no-param-reassign
txMeta = this.txStateManager.getTransaction(txMeta.id);
@ -960,10 +1004,8 @@ export default class TransactionController extends EventEmitter {
}
try {
const {
gasFeeEstimates,
gasEstimateType,
} = await this._getEIP1559GasFeeEstimates();
const { gasFeeEstimates, gasEstimateType } =
await this._getEIP1559GasFeeEstimates();
if (
eip1559Compatibility &&
gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET
@ -1025,11 +1067,8 @@ export default class TransactionController extends EventEmitter {
return { gasLimit: GAS_LIMITS.SIMPLE };
}
const {
blockGasLimit,
estimatedGasHex,
simulationFails,
} = await this.txGasUtil.analyzeGasUsage(txMeta);
const { blockGasLimit, estimatedGasHex, simulationFails } =
await this.txGasUtil.analyzeGasUsage(txMeta);
// add additional gas buffer to our estimation for safety
const gasLimit = this.txGasUtil.addGasBuffer(
@ -1203,6 +1242,7 @@ export default class TransactionController extends EventEmitter {
loadingDefaults: false,
status: TRANSACTION_STATUSES.APPROVED,
type: TRANSACTION_TYPES.RETRY,
originalType: originalTxMeta.type,
});
if (estimatedBaseFee) {
@ -1531,9 +1571,8 @@ export default class TransactionController extends EventEmitter {
const metricsParams = { gas_used: gasUsed };
if (submittedTime) {
metricsParams.completion_time = this._getTransactionCompletionTime(
submittedTime,
);
metricsParams.completion_time =
this._getTransactionCompletionTime(submittedTime);
}
if (txReceipt.status === '0x0') {
@ -1592,9 +1631,8 @@ export default class TransactionController extends EventEmitter {
const metricsParams = { gas_used: gasUsed };
if (submittedTime) {
metricsParams.completion_time = this._getTransactionCompletionTime(
submittedTime,
);
metricsParams.completion_time =
this._getTransactionCompletionTime(submittedTime);
}
if (txReceipt.status === '0x0') {
@ -1659,10 +1697,8 @@ export default class TransactionController extends EventEmitter {
*/
async createTransactionEventFragment(transactionId, event) {
const txMeta = this.txStateManager.getTransaction(transactionId);
const {
properties,
sensitiveProperties,
} = await this._buildEventFragmentProperties(txMeta);
const { properties, sensitiveProperties } =
await this._buildEventFragmentProperties(txMeta);
this._createTransactionEventFragment(
txMeta,
event,
@ -1840,6 +1876,7 @@ export default class TransactionController extends EventEmitter {
return;
}
otherTxMeta.replacedBy = txMeta.hash;
otherTxMeta.replacedById = txMeta.id;
this.txStateManager.updateTransaction(
txMeta,
'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce',
@ -1976,8 +2013,64 @@ export default class TransactionController extends EventEmitter {
}
}
/**
* The allowance amount in relation to the dapp proposed amount for specific token
*
* @param {string} transactionApprovalAmountType - The transaction approval amount type
* @param {string} originalApprovalAmount - The original approval amount is the originally dapp proposed token amount
* @param {string} finalApprovalAmount - The final approval amount is the chosen amount which will be the same as the
* originally dapp proposed token amount if the user does not edit the amount or will be a custom token amount set by the user
*/
_allowanceAmountInRelationToDappProposedValue(
transactionApprovalAmountType,
originalApprovalAmount,
finalApprovalAmount,
) {
if (
transactionApprovalAmountType ===
TRANSACTION_APPROVAL_AMOUNT_TYPE.CUSTOM &&
originalApprovalAmount &&
finalApprovalAmount
) {
return `${new BigNumber(originalApprovalAmount, 10)
.div(finalApprovalAmount, 10)
.times(100)
.round(2)}`;
}
return null;
}
/**
* The allowance amount in relation to the balance for that specific token
*
* @param {string} transactionApprovalAmountType - The transaction approval amount type
* @param {string} dappProposedTokenAmount - The dapp proposed token amount
* @param {string} currentTokenBalance - The balance of the token that is being send
*/
_allowanceAmountInRelationToTokenBalance(
transactionApprovalAmountType,
dappProposedTokenAmount,
currentTokenBalance,
) {
if (
(transactionApprovalAmountType ===
TRANSACTION_APPROVAL_AMOUNT_TYPE.CUSTOM ||
transactionApprovalAmountType ===
TRANSACTION_APPROVAL_AMOUNT_TYPE.DAPP_PROPOSED) &&
dappProposedTokenAmount &&
currentTokenBalance
) {
return `${new BigNumber(dappProposedTokenAmount, 16)
.div(currentTokenBalance, 10)
.times(100)
.round(2)}`;
}
return null;
}
async _buildEventFragmentProperties(txMeta, extraParams) {
const {
id,
type,
time,
status,
@ -1992,8 +2085,11 @@ export default class TransactionController extends EventEmitter {
estimateUsed,
},
defaultGasEstimates,
originalType,
replacedById,
metamaskNetworkId: network,
} = txMeta;
const { transactions } = this.store.getState();
const source = referrer === ORIGIN_METAMASK ? 'user' : 'dapp';
const { assetType, tokenStandard } = await determineTransactionAssetType(
@ -2035,7 +2131,8 @@ export default class TransactionController extends EventEmitter {
if (gasFeeEstimates?.[estimateType]?.suggestedMaxPriorityFeePerGas) {
defaultMaxPriorityFeePerGas =
gasFeeEstimates[estimateType]?.suggestedMaxPriorityFeePerGas;
gasParams.default_max_priority_fee_per_gas = defaultMaxPriorityFeePerGas;
gasParams.default_max_priority_fee_per_gas =
defaultMaxPriorityFeePerGas;
}
}
}
@ -2068,12 +2165,93 @@ export default class TransactionController extends EventEmitter {
eip1559Version = eip1559V2Enabled ? '2' : '1';
}
const properties = {
const contractInteractionTypes = [
TRANSACTION_TYPES.CONTRACT_INTERACTION,
TRANSACTION_TYPES.TOKEN_METHOD_APPROVE,
TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM,
TRANSACTION_TYPES.TOKEN_METHOD_SET_APPROVAL_FOR_ALL,
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
TRANSACTION_TYPES.SMART,
TRANSACTION_TYPES.SWAP,
TRANSACTION_TYPES.SWAP_APPROVAL,
].includes(type);
const contractMethodNames = {
APPROVE: 'Approve',
};
const customTokenAmount = transactions[id]?.customTokenAmount;
const dappProposedTokenAmount = transactions[id]?.dappProposedTokenAmount;
const currentTokenBalance = transactions[id]?.currentTokenBalance;
const originalApprovalAmount = transactions[id]?.originalApprovalAmount;
const finalApprovalAmount = transactions[id]?.finalApprovalAmount;
let transactionApprovalAmountType;
let transactionContractMethod;
let transactionApprovalAmountVsProposedRatio;
let transactionApprovalAmountVsBalanceRatio;
let transactionType = TRANSACTION_TYPES.SIMPLE_SEND;
if (type === TRANSACTION_TYPES.CANCEL) {
transactionType = TRANSACTION_TYPES.CANCEL;
} else if (type === TRANSACTION_TYPES.RETRY) {
transactionType = originalType;
} else if (type === TRANSACTION_TYPES.DEPLOY_CONTRACT) {
transactionType = TRANSACTION_TYPES.DEPLOY_CONTRACT;
} else if (contractInteractionTypes) {
transactionType = TRANSACTION_TYPES.CONTRACT_INTERACTION;
transactionContractMethod = transactions[id]?.contractMethodName;
if (
transactionContractMethod === contractMethodNames.APPROVE &&
tokenStandard === TOKEN_STANDARDS.ERC20
) {
if (dappProposedTokenAmount === '0' || customTokenAmount === '0') {
transactionApprovalAmountType =
TRANSACTION_APPROVAL_AMOUNT_TYPE.REVOKE;
} else if (customTokenAmount) {
transactionApprovalAmountType =
TRANSACTION_APPROVAL_AMOUNT_TYPE.CUSTOM;
} else if (dappProposedTokenAmount) {
transactionApprovalAmountType =
TRANSACTION_APPROVAL_AMOUNT_TYPE.DAPP_PROPOSED;
}
transactionApprovalAmountVsProposedRatio =
this._allowanceAmountInRelationToDappProposedValue(
transactionApprovalAmountType,
originalApprovalAmount,
finalApprovalAmount,
);
transactionApprovalAmountVsBalanceRatio =
this._allowanceAmountInRelationToTokenBalance(
transactionApprovalAmountType,
dappProposedTokenAmount,
currentTokenBalance,
);
}
}
const replacedTxMeta = this._getTransaction(replacedById);
const TRANSACTION_REPLACEMENT_METHODS = {
RETRY: TRANSACTION_TYPES.RETRY,
CANCEL: TRANSACTION_TYPES.CANCEL,
SAME_NONCE: 'other',
};
let transactionReplaced;
if (extraParams?.dropped) {
transactionReplaced = TRANSACTION_REPLACEMENT_METHODS.SAME_NONCE;
if (replacedTxMeta?.type === TRANSACTION_TYPES.CANCEL) {
transactionReplaced = TRANSACTION_REPLACEMENT_METHODS.CANCEL;
} else if (replacedTxMeta?.type === TRANSACTION_TYPES.RETRY) {
transactionReplaced = TRANSACTION_REPLACEMENT_METHODS.RETRY;
}
}
let properties = {
chain_id: chainId,
referrer,
source,
network,
type,
eip_1559_version: eip1559Version,
gas_edit_type: 'none',
gas_edit_attempted: 'none',
@ -2081,19 +2259,40 @@ export default class TransactionController extends EventEmitter {
device_model: await this.getDeviceModel(this.getSelectedAddress()),
asset_type: assetType,
token_standard: tokenStandard,
transaction_type: transactionType,
transaction_speed_up: type === TRANSACTION_TYPES.RETRY,
};
const sensitiveProperties = {
if (transactionContractMethod === contractMethodNames.APPROVE) {
properties = {
...properties,
transaction_approval_amount_type: transactionApprovalAmountType,
};
}
let sensitiveProperties = {
status,
transaction_envelope_type: isEIP1559Transaction(txMeta)
? TRANSACTION_ENVELOPE_TYPE_NAMES.FEE_MARKET
: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY,
first_seen: time,
gas_limit: gasLimit,
transaction_contract_method: transactionContractMethod,
transaction_replaced: transactionReplaced,
...extraParams,
...gasParamsInGwei,
};
if (transactionContractMethod === contractMethodNames.APPROVE) {
sensitiveProperties = {
...sensitiveProperties,
transaction_approval_amount_vs_balance_ratio:
transactionApprovalAmountVsBalanceRatio,
transaction_approval_amount_vs_proposed_ratio:
transactionApprovalAmountVsProposedRatio,
};
}
return { properties, sensitiveProperties };
}
@ -2228,10 +2427,8 @@ export default class TransactionController extends EventEmitter {
if (!txMeta) {
return;
}
const {
properties,
sensitiveProperties,
} = await this._buildEventFragmentProperties(txMeta, extraParams);
const { properties, sensitiveProperties } =
await this._buildEventFragmentProperties(txMeta, extraParams);
// Create event fragments for event types that spawn fragments, and ensure
// existence of fragments for event types that act upon them.
@ -2300,6 +2497,8 @@ export default class TransactionController extends EventEmitter {
_dropTransaction(txId) {
this.txStateManager.setTxStatusDropped(txId);
const txMeta = this.txStateManager.getTransaction(txId);
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.FINALIZED);
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.FINALIZED, {
dropped: true,
});
}
}

@ -17,6 +17,7 @@ import {
TRANSACTION_ENVELOPE_TYPES,
TRANSACTION_EVENTS,
ASSET_TYPES,
TOKEN_STANDARDS,
} from '../../../../shared/constants/transaction';
import { SECOND } from '../../../../shared/constants/time';
@ -26,7 +27,6 @@ import {
} from '../../../../shared/constants/gas';
import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../ui/helpers/constants/transactions';
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
import { TOKEN_STANDARDS } from '../../../../ui/helpers/constants/common';
import { ORIGIN_METAMASK } from '../../../../shared/constants/app';
import TransactionController from '.';
@ -334,11 +334,14 @@ describe('Transaction Controller', function () {
const selectedAddress = '0x1678a085c290ebd122dc42cba69373b5953b831d';
const recipientAddress = '0xc42edfcc21ed14dda456aa0756c153f7985d8813';
let getSelectedAddress, getPermittedAccounts;
let getSelectedAddress, getPermittedAccounts, getDefaultGasFees;
beforeEach(function () {
getSelectedAddress = sinon
.stub(txController, 'getSelectedAddress')
.returns(selectedAddress);
getDefaultGasFees = sinon
.stub(txController, '_getDefaultGasFees')
.returns({});
getPermittedAccounts = sinon
.stub(txController, 'getPermittedAccounts')
.returns([selectedAddress]);
@ -347,6 +350,7 @@ describe('Transaction Controller', function () {
afterEach(function () {
getSelectedAddress.restore();
getPermittedAccounts.restore();
getDefaultGasFees.restore();
});
it('should add an unapproved transaction and return a valid txMeta', async function () {
@ -372,6 +376,62 @@ describe('Transaction Controller', function () {
assert.deepEqual(txMeta, memTxMeta);
});
it('should add only 1 unapproved transaction when called twice with same actionId', async function () {
await txController.addUnapprovedTransaction(
{
from: selectedAddress,
to: recipientAddress,
},
undefined,
undefined,
undefined,
'12345',
);
const transactionCount1 =
txController.txStateManager.getTransactions().length;
await txController.addUnapprovedTransaction(
{
from: selectedAddress,
to: recipientAddress,
},
undefined,
undefined,
undefined,
'12345',
);
const transactionCount2 =
txController.txStateManager.getTransactions().length;
assert.equal(transactionCount1, transactionCount2);
});
it('should add multiple transactions when called with different actionId', async function () {
await txController.addUnapprovedTransaction(
{
from: selectedAddress,
to: recipientAddress,
},
undefined,
undefined,
undefined,
'12345',
);
const transactionCount1 =
txController.txStateManager.getTransactions().length;
await txController.addUnapprovedTransaction(
{
from: selectedAddress,
to: recipientAddress,
},
undefined,
undefined,
undefined,
'00000',
);
const transactionCount2 =
txController.txStateManager.getTransactions().length;
assert.equal(transactionCount1 + 1, transactionCount2);
});
it('should emit newUnapprovedTx event and pass txMeta as the first argument', function (done) {
providerResultStub.eth_gasPrice = '4a817c800';
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
@ -1471,17 +1531,20 @@ describe('Transaction Controller', function () {
network: '42',
referrer: ORIGIN_METAMASK,
source: EVENT.SOURCE.TRANSACTION.USER,
type: TRANSACTION_TYPES.SIMPLE_SEND,
transaction_type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
token_standard: TOKEN_STANDARDS.NONE,
device_model: 'N/A',
transaction_speed_up: false,
},
sensitiveProperties: {
default_gas: '0.000031501',
default_gas_price: '2',
gas_price: '2',
gas_limit: '0x7b0d',
transaction_contract_method: undefined,
transaction_replaced: undefined,
first_seen: 1624408066355,
transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY,
status: 'unapproved',
@ -1550,17 +1613,20 @@ describe('Transaction Controller', function () {
network: '42',
referrer: ORIGIN_METAMASK,
source: EVENT.SOURCE.TRANSACTION.USER,
type: TRANSACTION_TYPES.SIMPLE_SEND,
transaction_type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
token_standard: TOKEN_STANDARDS.NONE,
device_model: 'N/A',
transaction_speed_up: false,
},
sensitiveProperties: {
default_gas: '0.000031501',
default_gas_price: '2',
gas_price: '2',
gas_limit: '0x7b0d',
transaction_contract_method: undefined,
transaction_replaced: undefined,
first_seen: 1624408066355,
transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY,
status: 'unapproved',
@ -1639,17 +1705,20 @@ describe('Transaction Controller', function () {
network: '42',
referrer: 'other',
source: EVENT.SOURCE.TRANSACTION.DAPP,
type: TRANSACTION_TYPES.SIMPLE_SEND,
transaction_type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
token_standard: TOKEN_STANDARDS.NONE,
device_model: 'N/A',
transaction_speed_up: false,
},
sensitiveProperties: {
default_gas: '0.000031501',
default_gas_price: '2',
gas_price: '2',
gas_limit: '0x7b0d',
transaction_contract_method: undefined,
transaction_replaced: undefined,
first_seen: 1624408066355,
transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY,
status: 'unapproved',
@ -1720,17 +1789,20 @@ describe('Transaction Controller', function () {
network: '42',
referrer: 'other',
source: EVENT.SOURCE.TRANSACTION.DAPP,
type: TRANSACTION_TYPES.SIMPLE_SEND,
transaction_type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
token_standard: TOKEN_STANDARDS.NONE,
device_model: 'N/A',
transaction_speed_up: false,
},
sensitiveProperties: {
default_gas: '0.000031501',
default_gas_price: '2',
gas_price: '2',
gas_limit: '0x7b0d',
transaction_contract_method: undefined,
transaction_replaced: undefined,
first_seen: 1624408066355,
transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY,
status: 'unapproved',
@ -1801,15 +1873,18 @@ describe('Transaction Controller', function () {
network: '42',
referrer: 'other',
source: EVENT.SOURCE.TRANSACTION.DAPP,
type: TRANSACTION_TYPES.SIMPLE_SEND,
transaction_type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
token_standard: TOKEN_STANDARDS.NONE,
device_model: 'N/A',
transaction_speed_up: false,
},
sensitiveProperties: {
gas_price: '2',
gas_limit: '0x7b0d',
transaction_contract_method: undefined,
transaction_replaced: undefined,
first_seen: 1624408066355,
transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY,
status: 'unapproved',
@ -1860,7 +1935,7 @@ describe('Transaction Controller', function () {
network: '42',
referrer: 'other',
source: EVENT.SOURCE.TRANSACTION.DAPP,
type: TRANSACTION_TYPES.SIMPLE_SEND,
transaction_type: TRANSACTION_TYPES.SIMPLE_SEND,
chain_id: '0x2a',
eip_1559_version: '0',
gas_edit_attempted: 'none',
@ -1869,12 +1944,15 @@ describe('Transaction Controller', function () {
asset_type: ASSET_TYPES.NATIVE,
token_standard: TOKEN_STANDARDS.NONE,
device_model: 'N/A',
transaction_speed_up: false,
},
sensitiveProperties: {
baz: 3.0,
foo: 'bar',
gas_price: '2',
gas_limit: '0x7b0d',
transaction_contract_method: undefined,
transaction_replaced: undefined,
first_seen: 1624408066355,
transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY,
status: 'unapproved',
@ -1937,11 +2015,12 @@ describe('Transaction Controller', function () {
network: '42',
referrer: 'other',
source: EVENT.SOURCE.TRANSACTION.DAPP,
type: TRANSACTION_TYPES.SIMPLE_SEND,
transaction_type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
token_standard: TOKEN_STANDARDS.NONE,
device_model: 'N/A',
transaction_speed_up: false,
},
sensitiveProperties: {
baz: 3.0,
@ -1949,6 +2028,8 @@ describe('Transaction Controller', function () {
max_fee_per_gas: '2',
max_priority_fee_per_gas: '2',
gas_limit: '0x7b0d',
transaction_contract_method: undefined,
transaction_replaced: undefined,
first_seen: 1624408066355,
transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.FEE_MARKET,
status: 'unapproved',
@ -2277,8 +2358,7 @@ describe('Transaction Controller', function () {
providerResultStub.eth_getCode = '0xab';
// test update gasFees
await txController.updateEditableParams('1', {
data:
'0xa9059cbb000000000000000000000000e18035bf8712672935fdb4e5e431b1a0183d2dfc0000000000000000000000000000000000000000000000000de0b6b3a7640000',
data: '0xa9059cbb000000000000000000000000e18035bf8712672935fdb4e5e431b1a0183d2dfc0000000000000000000000000000000000000000000000000de0b6b3a7640000',
});
const result = txStateManager.getTransaction('1');
assert.equal(
@ -2302,8 +2382,7 @@ describe('Transaction Controller', function () {
// maxFeePerGas: '0x004',
to: VALID_ADDRESS,
from: VALID_ADDRESS,
data:
'0xa9059cbb000000000000000000000000e18035bf8712672935fdb4e5e431b1a0183d2dfc0000000000000000000000000000000000000000000000000de0b6b3a7640000',
data: '0xa9059cbb000000000000000000000000e18035bf8712672935fdb4e5e431b1a0183d2dfc0000000000000000000000000000000000000000000000000de0b6b3a7640000',
},
estimateUsed: '0x005',
estimatedBaseFee: '0x006',

@ -198,13 +198,8 @@ export default class PendingTransactionTracker extends EventEmitter {
try {
const transactionReceipt = await this.query.getTransactionReceipt(txHash);
if (transactionReceipt?.blockNumber) {
const {
baseFeePerGas,
timestamp: blockTimestamp,
} = await this.query.getBlockByHash(
transactionReceipt?.blockHash,
false,
);
const { baseFeePerGas, timestamp: blockTimestamp } =
await this.query.getBlockByHash(transactionReceipt?.blockHash, false);
this.emit(
'tx:confirmed',

@ -154,8 +154,7 @@ describe('PendingTransactionTracker', function () {
it('should call _checkPendingTx for each pending transaction', async function () {
const txMeta = {
id: 1,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.SIGNED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
@ -212,8 +211,7 @@ describe('PendingTransactionTracker', function () {
it('should publish a new transaction', async function () {
const txMeta = {
id: 1,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.SIGNED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
@ -257,8 +255,7 @@ describe('PendingTransactionTracker', function () {
it('should publish the given transaction if more than 2**retryCount blocks have passed', async function () {
const txMeta = {
id: 1,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.SIGNED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
@ -304,8 +301,7 @@ describe('PendingTransactionTracker', function () {
it('should NOT publish the given transaction if fewer than 2**retryCount blocks have passed', async function () {
const txMeta = {
id: 1,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.SIGNED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
@ -404,8 +400,7 @@ describe('PendingTransactionTracker', function () {
assert.ok(
await pendingTxTracker._checkIfTxWasDropped({
id: 1,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.SUBMITTED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
@ -437,8 +432,7 @@ describe('PendingTransactionTracker', function () {
const dropped = await pendingTxTracker._checkIfTxWasDropped({
id: 1,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.SUBMITTED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
@ -458,8 +452,7 @@ describe('PendingTransactionTracker', function () {
const confirmedTxList = [
{
id: 1,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.CONFIRMED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
@ -471,8 +464,7 @@ describe('PendingTransactionTracker', function () {
},
{
id: 2,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.CONFIRMED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
@ -517,8 +509,7 @@ describe('PendingTransactionTracker', function () {
const confirmedTxList = [
{
id: 1,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.CONFIRMED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
@ -530,8 +521,7 @@ describe('PendingTransactionTracker', function () {
},
{
id: 2,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.CONFIRMED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
@ -577,8 +567,7 @@ describe('PendingTransactionTracker', function () {
it("should emit 'tx:warning' if getTransactionReceipt rejects", async function () {
const txMeta = {
id: 1,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.SUBMITTED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
@ -730,8 +719,7 @@ describe('PendingTransactionTracker', function () {
txParams: { nonce: '0x1' },
id: '123',
value: '0x02',
hash:
'0x2a919d2512ec963f524bfd9730fb66b6d5a2e399d1dd957abb5e2b544a12644b',
hash: '0x2a919d2512ec963f524bfd9730fb66b6d5a2e399d1dd957abb5e2b544a12644b',
},
];
const pendingTxTracker = new PendingTransactionTracker({
@ -774,8 +762,7 @@ describe('PendingTransactionTracker', function () {
const nonceBN = new BN(2);
const txMeta = {
id: 1,
hash:
'0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: TRANSACTION_STATUSES.SUBMITTED,
txParams: {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',

@ -102,11 +102,8 @@ export default class TxGasUtil {
}
async getBufferedGasLimit(txMeta, multiplier) {
const {
blockGasLimit,
estimatedGasHex,
simulationFails,
} = await this.analyzeGasUsage(txMeta);
const { blockGasLimit, estimatedGasHex, simulationFails } =
await this.analyzeGasUsage(txMeta);
// add additional gas buffer to our estimation for safety
const gasLimit = this.addGasBuffer(

@ -1,7 +1,7 @@
import EventEmitter from 'safe-event-emitter';
import { ObservableStore } from '@metamask/obs-store';
import log from 'loglevel';
import { keyBy, mapValues, omitBy, pickBy, sortBy } from 'lodash';
import { values, keyBy, mapValues, omitBy, pickBy, sortBy } from 'lodash';
import createId from '../../../../shared/modules/random-id';
import { TRANSACTION_STATUSES } from '../../../../shared/constants/transaction';
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
@ -201,6 +201,21 @@ export default class TransactionStateManager extends EventEmitter {
return this.getTransactions({ searchCriteria });
}
/**
* Get transaction with provided.
*
* @param {string} [actionId]
* @returns {TransactionMeta} the filtered transaction
*/
getTransactionWithActionId(actionId) {
return values(
pickBy(
this.store.getState().transactions,
(transaction) => transaction.actionId === actionId,
),
)[0];
}
/**
* Adds the txMeta to the list of transactions in the store.
* if the list is over txHistoryLimit it will remove a transaction that

@ -2,7 +2,7 @@
// eslint-disable-next-line import/unambiguous
if (
!(typeof process !== 'undefined' && process.env.METAMASK_DEBUG) &&
typeof console !== undefined
typeof console !== 'undefined'
) {
console.log = noop;
console.info = noop;

@ -34,6 +34,7 @@ cleanContextForImports();
import log from 'loglevel';
import { WindowPostMessageStream } from '@metamask/post-message-stream';
import { initializeProvider } from '@metamask/providers/dist/initializeInpageProvider';
import shouldInjectProvider from '../../shared/modules/provider-injection';
restoreContextAfterImports();
@ -43,6 +44,7 @@ log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn');
// setup plugin communication
//
if (shouldInjectProvider()) {
// setup background connection
const metamaskStream = new WindowPostMessageStream({
name: 'metamask-inpage',
@ -54,3 +56,4 @@ initializeProvider({
logger: log,
shouldShimWeb3: true,
});
}

@ -16,7 +16,7 @@ export default class ComposableObservableStore extends ObservableStore {
* extends one of the two base controllers in the `@metamask/controllers`
* package.
*
* @type {Record<string, Object>}
* @type {Record<string, object>}
*/
config = {};
@ -43,7 +43,7 @@ export default class ComposableObservableStore extends ObservableStore {
/**
* Composes a new internal store subscription structure
*
* @param {Record<string, Object>} config - Describes which stores are being
* @param {Record<string, object>} config - Describes which stores are being
* composed. The key is the name of the store, and the value is either an
* ObserableStore, or a controller that extends one of the two base
* controllers in the `@metamask/controllers` package.

@ -18,7 +18,15 @@ import {
MAINNET_CHAIN_ID,
RINKEBY_CHAIN_ID,
ROPSTEN_CHAIN_ID,
SEPOLIA_CHAIN_ID,
KOVAN_CHAIN_ID,
GOERLI_CHAIN_ID,
BSC_CHAIN_ID,
OPTIMISM_CHAIN_ID,
POLYGON_CHAIN_ID,
AVALANCHE_CHAIN_ID,
FANTOM_CHAIN_ID,
ARBITRUM_CHAIN_ID,
} from '../../../shared/constants/network';
import {
@ -26,6 +34,14 @@ import {
SINGLE_CALL_BALANCES_ADDRESS_RINKEBY,
SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN,
SINGLE_CALL_BALANCES_ADDRESS_KOVAN,
SINGLE_CALL_BALANCES_ADDRESS_GOERLI,
SINGLE_CALL_BALANCES_ADDRESS_SEPOLIA,
SINGLE_CALL_BALANCES_ADDRESS_BSC,
SINGLE_CALL_BALANCES_ADDRESS_OPTIMISM,
SINGLE_CALL_BALANCES_ADDRESS_POLYGON,
SINGLE_CALL_BALANCES_ADDRESS_AVALANCHE,
SINGLE_CALL_BALANCES_ADDRESS_FANTOM,
SINGLE_CALL_BALANCES_ADDRESS_ARBITRUM,
} from '../constants/contracts';
import { bnToHex } from './util';
@ -230,6 +246,62 @@ export default class AccountTracker {
);
break;
case GOERLI_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS_GOERLI,
);
break;
case SEPOLIA_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS_SEPOLIA,
);
break;
case BSC_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS_BSC,
);
break;
case OPTIMISM_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS_OPTIMISM,
);
break;
case POLYGON_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS_POLYGON,
);
break;
case AVALANCHE_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS_AVALANCHE,
);
break;
case FANTOM_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS_FANTOM,
);
break;
case ARBITRUM_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS_ARBITRUM,
);
break;
default:
await Promise.all(addresses.map(this._updateAccount.bind(this)));
}
@ -243,8 +315,17 @@ export default class AccountTracker {
* @returns {Promise} after the account balance is updated
*/
async _updateAccount(address) {
let balance = '0x0';
// query balance
const balance = await this._query.getBalance(address);
try {
balance = await this._query.getBalance(address);
} catch (error) {
if (error.data?.request?.method !== 'eth_getBalance') {
throw error;
}
}
const result = { address, balance };
// update accounts state
const { accounts } = this.store.getState();

@ -7,9 +7,9 @@ import {
MAINNET_CHAIN_ID,
RINKEBY_CHAIN_ID,
ROPSTEN_CHAIN_ID,
SEPOLIA_CHAIN_ID,
BUYABLE_CHAINS_MAP,
} from '../../../shared/constants/network';
import { SECOND } from '../../../shared/constants/time';
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout';
import {
TRANSAK_API_KEY,
@ -17,7 +17,7 @@ import {
COINBASEPAY_API_KEY,
} from '../constants/on-ramp';
const fetchWithTimeout = getFetchWithTimeout(SECOND * 30);
const fetchWithTimeout = getFetchWithTimeout();
/**
* Create a Wyre purchase URL.
@ -83,9 +83,8 @@ const createTransakUrl = (walletAddress, chainId) => {
* @returns String
*/
const createMoonPayUrl = async (walletAddress, chainId) => {
const {
moonPay: { defaultCurrencyCode, showOnlyCurrencies } = {},
} = BUYABLE_CHAINS_MAP[chainId];
const { moonPay: { defaultCurrencyCode, showOnlyCurrencies } = {} } =
BUYABLE_CHAINS_MAP[chainId];
const moonPayQueryParams = new URLSearchParams({
apiKey: MOONPAY_API_KEY,
walletAddress,
@ -172,6 +171,8 @@ export default async function getBuyUrl({ chainId, address, service }) {
return 'https://github.com/kovan-testnet/faucet';
case 'goerli-faucet':
return 'https://goerli-faucet.slock.it/';
case 'sepolia-faucet':
return 'https://faucet.sepolia.dev/';
default:
throw new Error(
`Unknown cryptocurrency exchange or faucet: "${service}"`,
@ -191,6 +192,8 @@ function getDefaultServiceForChain(chainId) {
return 'kovan-faucet';
case GOERLI_CHAIN_ID:
return 'goerli-faucet';
case SEPOLIA_CHAIN_ID:
return 'sepolia-faucet';
default:
throw new Error(
`No default cryptocurrency exchange or faucet for chainId: "${chainId}"`,

@ -116,9 +116,8 @@ describe('buy-url', () => {
});
it('returns a MoonPay url with a prefilled wallet address for the Ethereum network', async () => {
const {
moonPay: { defaultCurrencyCode, showOnlyCurrencies } = {},
} = BUYABLE_CHAINS_MAP[MAINNET.chainId];
const { moonPay: { defaultCurrencyCode, showOnlyCurrencies } = {} } =
BUYABLE_CHAINS_MAP[MAINNET.chainId];
const moonPayQueryParams = new URLSearchParams({
apiKey: MOONPAY_API_KEY,
walletAddress: MAINNET.address,

@ -35,9 +35,8 @@ export default async function resolveEnsToIpfsContentId({ provider, name }) {
const type = contentHash.getCodec(rawContentHash);
if (type === 'ipfs-ns' || type === 'ipns-ns') {
decodedContentHash = contentHash.helpers.cidV0ToV1Base32(
decodedContentHash,
);
decodedContentHash =
contentHash.helpers.cidV0ToV1Base32(decodedContentHash);
}
return { type, hash: decodedContentHash };
@ -80,6 +79,7 @@ function getRegistryForChainId(chainId) {
case 3:
case 4:
case 5:
case 6:
// Mainnet, Ropsten, Rinkeby, and Goerli, respectively, use the same address
return '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e';
default:

@ -2,11 +2,10 @@ import base32Encode from 'base32-encode';
import base64 from 'base64-js';
import browser from 'webextension-polyfill';
import { SECOND } from '../../../../shared/constants/time';
import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout';
import resolveEnsToIpfsContentId from './resolver';
const fetchWithTimeout = getFetchWithTimeout(SECOND * 30);
const fetchWithTimeout = getFetchWithTimeout();
const supportedTopLevelDomains = ['eth'];

@ -3,6 +3,8 @@ import SafeEventEmitter from 'safe-event-emitter';
import createRandomId from '../../../shared/modules/random-id';
import { TEN_SECONDS_IN_MILLISECONDS } from '../../../ui/helpers/constants/critical-error';
class DisconnectError extends Error {}
class MetaRPCClient {
constructor(connectionStream) {
this.connectionStream = connectionStream;
@ -12,6 +14,7 @@ class MetaRPCClient {
this.connectionStream.on('data', this.handleResponse.bind(this));
this.connectionStream.on('end', this.close.bind(this));
this.responseHandled = {};
this.DisconnectError = DisconnectError;
}
send(id, payload, cb) {
@ -47,6 +50,13 @@ class MetaRPCClient {
close() {
this.notificationChannel.removeAllListeners();
this.uncaughtErrorChannel.removeAllListeners();
// fail all unfinished requests
for (const [id, handler] of this.requests) {
if (!this.responseHandled[id]) {
this.responseHandled[id] = true;
handler(new DisconnectError('disconnected'));
}
}
}
handleResponse(data) {

@ -1,3 +1,4 @@
/* eslint-disable jest/no-done-callback */
import { obj as createThoughStream } from 'through2';
import metaRPCClientFactory from './metaRPCClientFactory';
@ -9,13 +10,14 @@ describe('metaRPCClientFactory', () => {
const metaRPCClient = metaRPCClientFactory(streamTest);
metaRPCClient.foo();
});
it('should be able to make an rpc request/response with the method and params and node-style callback', () => {
it('should be able to make an rpc request/response with the method and params and node-style callback', (done) => {
const streamTest = createThoughStream();
const metaRPCClient = metaRPCClientFactory(streamTest);
// make a "foo" method call
metaRPCClient.foo('bar', (_, result) => {
expect(result).toStrictEqual('foobarbaz');
done();
});
// fake a response
@ -27,7 +29,7 @@ describe('metaRPCClientFactory', () => {
});
});
});
it('should be able to make an rpc request/error with the method and params and node-style callback', () => {
it('should be able to make an rpc request/error with the method and params and node-style callback', (done) => {
const streamTest = createThoughStream();
const metaRPCClient = metaRPCClientFactory(streamTest);
@ -35,6 +37,7 @@ describe('metaRPCClientFactory', () => {
metaRPCClient.foo('bar', (err) => {
expect(err.message).toStrictEqual('foo-message');
expect(err.code).toStrictEqual(1);
done();
});
metaRPCClient.requests.forEach((_, key) => {
@ -49,7 +52,7 @@ describe('metaRPCClientFactory', () => {
});
});
it('should be able to make an rpc request/response with the method and params and node-style callback with multiple instances of metaRPCClientFactory and the same connectionStream', () => {
it('should be able to make an rpc request/response with the method and params and node-style callback with multiple instances of metaRPCClientFactory and the same connectionStream', (done) => {
const streamTest = createThoughStream();
const metaRPCClient = metaRPCClientFactory(streamTest);
const metaRPCClient2 = metaRPCClientFactory(streamTest);
@ -59,6 +62,7 @@ describe('metaRPCClientFactory', () => {
expect(result).toStrictEqual('foobarbaz');
metaRPCClient2.baz('bar', (err) => {
expect(err).toBeNull();
done();
});
});
@ -81,12 +85,13 @@ describe('metaRPCClientFactory', () => {
});
});
it('should be able to handle notifications', () => {
it('should be able to handle notifications', (done) => {
const streamTest = createThoughStream();
const metaRPCClient = metaRPCClientFactory(streamTest);
metaRPCClient.onNotification((notification) => {
expect(notification.method).toStrictEqual('foobarbaz');
done();
});
// send a notification
@ -97,12 +102,13 @@ describe('metaRPCClientFactory', () => {
});
});
it('should be able to handle errors with no id', () => {
it('should be able to handle errors with no id', (done) => {
const streamTest = createThoughStream();
const metaRPCClient = metaRPCClientFactory(streamTest);
metaRPCClient.onUncaughtError((error) => {
expect(error.code).toStrictEqual(1);
done();
});
streamTest.write({
@ -114,12 +120,13 @@ describe('metaRPCClientFactory', () => {
});
});
it('should be able to handle errors with null id', () => {
it('should be able to handle errors with null id', (done) => {
const streamTest = createThoughStream();
const metaRPCClient = metaRPCClientFactory(streamTest);
metaRPCClient.onUncaughtError((error) => {
expect(error.code).toStrictEqual(1);
done();
});
streamTest.write({
@ -132,7 +139,7 @@ describe('metaRPCClientFactory', () => {
});
});
it('should be able to handle no message within TIMEOUT secs', async () => {
it('should be able to handle no message within TIMEOUT secs for getState', async () => {
jest.useFakeTimers();
const streamTest = createThoughStream();
const metaRPCClient = metaRPCClientFactory(streamTest);
@ -148,4 +155,17 @@ describe('metaRPCClientFactory', () => {
jest.useRealTimers();
});
it('should fail all pending actions with a DisconnectError when the stream ends', (done) => {
const streamTest = createThoughStream();
const metaRPCClient = metaRPCClientFactory(streamTest);
metaRPCClient.foo('bar', (err) => {
expect(err).toBeInstanceOf(metaRPCClient.DisconnectError);
expect(err.message).toStrictEqual('disconnected');
done();
});
streamTest.emit('end');
});
});

@ -1,8 +1,7 @@
import log from 'loglevel';
import { SECOND } from '../../../shared/constants/time';
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout';
const fetchWithTimeout = getFetchWithTimeout(SECOND * 30);
const fetchWithTimeout = getFetchWithTimeout();
const FIXTURE_SERVER_HOST = 'localhost';
const FIXTURE_SERVER_PORT = 12345;

@ -7,6 +7,7 @@ import { MESSAGE_TYPE } from '../../../shared/constants/app';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import createId from '../../../shared/modules/random-id';
import { EVENT } from '../../../shared/constants/metametrics';
import { detectSIWE } from '../../../shared/modules/siwe';
import { addHexPrefix } from './util';
const hexRe = /^[0-9A-Fa-f]+$/gu;
@ -135,6 +136,11 @@ export default class PersonalMessageManager extends EventEmitter {
msgParams.origin = req.origin;
}
msgParams.data = this.normalizeMsgData(msgParams.data);
// check for SIWE message
const siwe = detectSIWE(msgParams);
msgParams.siwe = siwe;
// create txData obj with parameters and meta data
const time = new Date().getTime();
const msgId = createId();
@ -314,8 +320,9 @@ export default class PersonalMessageManager extends EventEmitter {
*/
_saveMsgList() {
const unapprovedPersonalMsgs = this.getUnapprovedMsgs();
const unapprovedPersonalMsgCount = Object.keys(unapprovedPersonalMsgs)
.length;
const unapprovedPersonalMsgCount = Object.keys(
unapprovedPersonalMsgs,
).length;
this.memStore.updateState({
unapprovedPersonalMsgs,
unapprovedPersonalMsgCount,

@ -2,7 +2,7 @@
import { handlers as permittedSnapMethods } from '@metamask/rpc-methods/dist/permitted';
///: END:ONLY_INCLUDE_IN
import { permissionRpcMethods } from '@metamask/controllers';
import { selectHooks } from '@metamask/rpc-methods';
import { selectHooks } from '@metamask/rpc-methods/dist/utils';
import { ethErrors } from 'eth-rpc-errors';
import { flatten } from 'lodash';
import { UNSUPPORTED_RPC_METHODS } from '../../../../shared/constants/network';
@ -31,7 +31,7 @@ const expectedHookNames = Array.from(
*
* @param {Record<string, unknown>} hooks - Required "hooks" into our
* controllers.
* @returns {(req: Object, res: Object, next: Function, end: Function) => void}
* @returns {(req: object, res: object, next: Function, end: Function) => void}
*/
export function createMethodMiddleware(hooks) {
// Fail immediately if we forgot to provide any expected hooks.

@ -289,25 +289,34 @@ async function addEthereumChainHandler(
}),
);
let rpcUrlOrigin;
try {
rpcUrlOrigin = new URL(firstValidRPCUrl).origin;
} catch {
// ignore
}
sendMetrics({
event: 'Custom Network Added',
category: EVENT.CATEGORIES.NETWORK,
referrer: {
url: origin,
},
sensitiveProperties: {
properties: {
chain_id: _chainId,
rpc_url: firstValidRPCUrl,
network_name: _chainName,
// Including network to override the default network
// property included in all events. For RPC type networks
// the MetaMetrics controller uses the rpcUrl for the network
// property.
network: firstValidRPCUrl,
network: rpcUrlOrigin,
symbol: ticker,
block_explorer_url: firstValidBlockExplorerUrl,
source: EVENT.SOURCE.TRANSACTION.DAPP,
},
sensitiveProperties: {
rpc_url: rpcUrlOrigin,
},
});
// Once the network has been added, the requested is considered successful

@ -11,6 +11,7 @@ const METAMASK_DEBUG = process.env.METAMASK_DEBUG;
const METAMASK_ENVIRONMENT = process.env.METAMASK_ENVIRONMENT;
const SENTRY_DSN_DEV = process.env.SENTRY_DSN_DEV;
const METAMASK_BUILD_TYPE = process.env.METAMASK_BUILD_TYPE;
const IN_TEST = process.env.IN_TEST;
/* eslint-enable prefer-destructuring */
// This describes the subset of Redux state attached to errors sent to Sentry
@ -71,7 +72,13 @@ export const SENTRY_STATE = {
export default function setupSentry({ release, getState }) {
if (!release) {
throw new Error('Missing release');
} else if (METAMASK_DEBUG) {
} else if (METAMASK_DEBUG && !IN_TEST) {
/**
* Workaround until the following issue is resolved
* https://github.com/MetaMask/metamask-extension/issues/15691
* The IN_TEST condition allows the e2e tests to run with both
* yarn start:test and yarn build:test
*/
return undefined;
}

@ -391,8 +391,9 @@ export default class TypedMessageManager extends EventEmitter {
*/
_saveMsgList() {
const unapprovedTypedMessages = this.getUnapprovedMsgs();
const unapprovedTypedMessagesCount = Object.keys(unapprovedTypedMessages)
.length;
const unapprovedTypedMessagesCount = Object.keys(
unapprovedTypedMessages,
).length;
this.memStore.updateState({
unapprovedTypedMessages,
unapprovedTypedMessagesCount,

@ -0,0 +1,241 @@
import { strict as assert } from 'assert';
import sinon from 'sinon';
import proxyquire from 'proxyquire';
import { ORIGIN_METAMASK } from '../../shared/constants/app';
const Ganache = require('../../test/e2e/ganache');
const ganacheServer = new Ganache();
const browserPolyfillMock = {
runtime: {
id: 'fake-extension-id',
onInstalled: {
addListener: () => undefined,
},
onMessageExternal: {
addListener: () => undefined,
},
getPlatformInfo: async () => 'mac',
},
};
let loggerMiddlewareMock;
const createLoggerMiddlewareMock = () => (req, res, next) => {
if (loggerMiddlewareMock) {
loggerMiddlewareMock.requests.push(req);
next((cb) => {
loggerMiddlewareMock.responses.push(res);
cb();
});
return;
}
next();
};
const TEST_SEED =
'debris dizzy just program just float decrease vacant alarm reduce speak stadium';
const MetaMaskController = proxyquire('./metamask-controller', {
'./lib/createLoggerMiddleware': { default: createLoggerMiddlewareMock },
}).default;
describe('MetaMaskController', function () {
let metamaskController;
const sandbox = sinon.createSandbox();
const noop = () => undefined;
before(async function () {
await ganacheServer.start();
});
beforeEach(function () {
metamaskController = new MetaMaskController({
showUserConfirmation: noop,
encryptor: {
encrypt(_, object) {
this.object = object;
return Promise.resolve('mock-encrypted');
},
decrypt() {
return Promise.resolve(this.object);
},
},
initLangCode: 'en_US',
platform: {
showTransactionNotification: () => undefined,
getVersion: () => 'foo',
},
browser: browserPolyfillMock,
infuraProjectId: 'foo',
});
});
afterEach(function () {
sandbox.restore();
});
after(async function () {
await ganacheServer.quit();
});
describe('#addNewAccount', function () {
it('two parallel calls with same accountCount give same result', async function () {
await metamaskController.createNewVaultAndKeychain('test@123');
const [addNewAccountResult1, addNewAccountResult2] = await Promise.all([
metamaskController.addNewAccount(1),
metamaskController.addNewAccount(1),
]);
assert.deepEqual(
Object.keys(addNewAccountResult1.identities),
Object.keys(addNewAccountResult2.identities),
);
});
it('two successive calls with same accountCount give same result', async function () {
await metamaskController.createNewVaultAndKeychain('test@123');
const addNewAccountResult1 = await metamaskController.addNewAccount(1);
const addNewAccountResult2 = await metamaskController.addNewAccount(1);
assert.deepEqual(
Object.keys(addNewAccountResult1.identities),
Object.keys(addNewAccountResult2.identities),
);
});
it('two successive calls with different accountCount give different results', async function () {
await metamaskController.createNewVaultAndKeychain('test@123');
const addNewAccountResult1 = await metamaskController.addNewAccount(1);
const addNewAccountResult2 = await metamaskController.addNewAccount(2);
assert.notDeepEqual(addNewAccountResult1, addNewAccountResult2);
});
});
describe('#importAccountWithStrategy', function () {
it('two sequential calls with same strategy give same result', async function () {
let keyringControllerState1;
let keyringControllerState2;
const importPrivkey =
'4cfd3e90fc78b0f86bf7524722150bb8da9c60cd532564d7ff43f5716514f553';
await metamaskController.createNewVaultAndKeychain('test@123');
await Promise.all([
metamaskController.importAccountWithStrategy('Private Key', [
importPrivkey,
]),
Promise.resolve(1).then(() => {
keyringControllerState1 = JSON.stringify(
metamaskController.keyringController.memStore.getState(),
);
metamaskController.importAccountWithStrategy('Private Key', [
importPrivkey,
]);
}),
Promise.resolve(2).then(() => {
keyringControllerState2 = JSON.stringify(
metamaskController.keyringController.memStore.getState(),
);
}),
]);
assert.deepEqual(keyringControllerState1, keyringControllerState2);
});
});
describe('#createNewVaultAndRestore', function () {
it('two successive calls with same inputs give same result', async function () {
const result1 = await metamaskController.createNewVaultAndRestore(
'test@123',
TEST_SEED,
);
const result2 = await metamaskController.createNewVaultAndRestore(
'test@123',
TEST_SEED,
);
assert.deepEqual(result1, result2);
});
});
describe('#createNewVaultAndKeychain', function () {
it('two successive calls with same inputs give same result', async function () {
const result1 = await metamaskController.createNewVaultAndKeychain(
'test@123',
);
const result2 = await metamaskController.createNewVaultAndKeychain(
'test@123',
);
assert.notEqual(result1, undefined);
assert.deepEqual(result1, result2);
});
});
describe('#addToken', function () {
const address = '0x514910771af9ca656af840dff83e8264ecf986ca';
const symbol = 'LINK';
const decimals = 18;
it('two parallel calls with same token details give same result', async function () {
const supportsInterfaceStub = sinon
.stub()
.returns(Promise.resolve(false));
sinon
.stub(metamaskController.tokensController, '_createEthersContract')
.callsFake(() =>
Promise.resolve({ supportsInterface: supportsInterfaceStub }),
);
const [token1, token2] = await Promise.all([
metamaskController.getApi().addToken(address, symbol, decimals),
metamaskController.getApi().addToken(address, symbol, decimals),
]);
assert.deepEqual(token1, token2);
});
});
describe('#addCustomNetwork', function () {
const customRpc = {
chainId: '0x1',
chainName: 'DUMMY_CHAIN_NAME',
rpcUrl: 'DUMMY_RPCURL',
ticker: 'DUMMY_TICKER',
blockExplorerUrl: 'DUMMY_EXPLORER',
};
it('two successive calls with custom RPC details give same result', async function () {
await metamaskController.addCustomNetwork(customRpc);
const rpcList1Length =
metamaskController.preferencesController.store.getState()
.frequentRpcListDetail.length;
await metamaskController.addCustomNetwork(customRpc);
const rpcList2Length =
metamaskController.preferencesController.store.getState()
.frequentRpcListDetail.length;
assert.equal(rpcList1Length, rpcList2Length);
});
});
describe('#updateTransactionSendFlowHistory', function () {
it('two sequential calls with same history give same result', async function () {
const recipientAddress = '0xc42edfcc21ed14dda456aa0756c153f7985d8813';
await metamaskController.createNewVaultAndKeychain('test@123');
const accounts = await metamaskController.keyringController.getAccounts();
const txMeta = await metamaskController.getApi().addUnapprovedTransaction(
{
from: accounts[0],
to: recipientAddress,
},
ORIGIN_METAMASK,
);
const [transaction1, transaction2] = await Promise.all([
metamaskController
.getApi()
.updateTransactionSendFlowHistory(txMeta.id, 2, ['foo1', 'foo2']),
Promise.resolve(1).then(() =>
metamaskController
.getApi()
.updateTransactionSendFlowHistory(txMeta.id, 2, ['foo1', 'foo2']),
),
]);
assert.deepEqual(transaction1, transaction2);
});
});
});

File diff suppressed because it is too large Load Diff

@ -221,7 +221,8 @@ describe('MetaMaskController', function () {
});
it('adds private key to keyrings in KeyringController', async function () {
const simpleKeyrings = metamaskController.keyringController.getKeyringsByType(
const simpleKeyrings =
metamaskController.keyringController.getKeyringsByType(
'Simple Key Pair',
);
const privKeyBuffer = simpleKeyrings[0].wallets[0].privateKey;
@ -234,7 +235,8 @@ describe('MetaMaskController', function () {
});
it('adds 1 account', async function () {
const keyringAccounts = await metamaskController.keyringController.getAccounts();
const keyringAccounts =
await metamaskController.keyringController.getAccounts();
assert.equal(
keyringAccounts[keyringAccounts.length - 1],
'0xe18035bf8712672935fdb4e5e431b1a0183d2dfc',
@ -259,7 +261,8 @@ describe('MetaMaskController', function () {
const identities = Object.keys(
metamaskController.preferencesController.store.getState().identities,
);
const addresses = await metamaskController.keyringController.getAccounts();
const addresses =
await metamaskController.keyringController.getAccounts();
identities.forEach((identity) => {
assert.ok(
@ -486,7 +489,8 @@ describe('MetaMaskController', function () {
});
it('changes preferences controller select address', function () {
const preferenceControllerState = metamaskController.preferencesController.store.getState();
const preferenceControllerState =
metamaskController.preferencesController.store.getState();
assert.equal(preferenceControllerState.selectedAddress, address);
});
@ -517,7 +521,8 @@ describe('MetaMaskController', function () {
await metamaskController
.connectHardware(DEVICE_NAMES.TREZOR, 0)
.catch(() => null);
const keyrings = await metamaskController.keyringController.getKeyringsByType(
const keyrings =
await metamaskController.keyringController.getKeyringsByType(
KEYRING_TYPES.TREZOR,
);
assert.deepEqual(
@ -532,7 +537,8 @@ describe('MetaMaskController', function () {
await metamaskController
.connectHardware(DEVICE_NAMES.LEDGER, 0)
.catch(() => null);
const keyrings = await metamaskController.keyringController.getKeyringsByType(
const keyrings =
await metamaskController.keyringController.getKeyringsByType(
KEYRING_TYPES.LEDGER,
);
assert.deepEqual(
@ -586,7 +592,8 @@ describe('MetaMaskController', function () {
.connectHardware(DEVICE_NAMES.TREZOR, 0)
.catch(() => null);
await metamaskController.forgetDevice(DEVICE_NAMES.TREZOR);
const keyrings = await metamaskController.keyringController.getKeyringsByType(
const keyrings =
await metamaskController.keyringController.getKeyringsByType(
KEYRING_TYPES.TREZOR,
);
@ -645,7 +652,8 @@ describe('MetaMaskController', function () {
});
it('should set unlockedAccount in the keyring', async function () {
const keyrings = await metamaskController.keyringController.getKeyringsByType(
const keyrings =
await metamaskController.keyringController.getKeyringsByType(
KEYRING_TYPES.TREZOR,
);
assert.equal(keyrings[0].unlockedAccount, accountToUnlock);
@ -690,7 +698,8 @@ describe('MetaMaskController', function () {
CUSTOM_RPC_URL,
CUSTOM_RPC_CHAIN_ID,
);
const networkControllerState = metamaskController.networkController.store.getState();
const networkControllerState =
metamaskController.networkController.store.getState();
assert.equal(networkControllerState.provider.rpcUrl, CUSTOM_RPC_URL);
});
});
@ -725,8 +734,9 @@ describe('MetaMaskController', function () {
});
it('#addNewAccount', async function () {
await metamaskController.addNewAccount();
const getAccounts = await metamaskController.keyringController.getAccounts();
await metamaskController.addNewAccount(1);
const getAccounts =
await metamaskController.keyringController.getAccounts();
assert.equal(getAccounts.length, 2);
});
});
@ -959,7 +969,8 @@ describe('MetaMaskController', function () {
// handle the promise so it doesn't throw an unhandledRejection
promise.then(noop).catch(noop);
metamaskPersonalMsgs = metamaskController.personalMessageManager.getUnapprovedMsgs();
metamaskPersonalMsgs =
metamaskController.personalMessageManager.getUnapprovedMsgs();
personalMessages = metamaskController.personalMessageManager.messages;
msgId = Object.keys(metamaskPersonalMsgs)[0];
personalMessages[0].msgParams.metamaskId = parseInt(msgId, 10);
@ -1250,9 +1261,8 @@ describe('MetaMaskController', function () {
describe('markNotificationsAsRead', function () {
it('marks the notification as read', function () {
metamaskController.markNotificationsAsRead([NOTIFICATION_ID]);
const readNotification = metamaskController.getState().notifications[
NOTIFICATION_ID
];
const readNotification =
metamaskController.getState().notifications[NOTIFICATION_ID];
assert.notEqual(readNotification.readDate, null);
});
});

@ -18,11 +18,8 @@ const storage = {
describe('storage is migrated successfully where transactions that are submitted have submittedTimes', () => {
it('should add submittedTime key on the txMeta if appropriate', async () => {
const migratedData = await migration22.migrate(storage);
const [
txMeta1,
txMeta2,
txMeta3,
] = migratedData.data.TransactionController.transactions;
const [txMeta1, txMeta2, txMeta3] =
migratedData.data.TransactionController.transactions;
expect(migratedData.meta.version).toStrictEqual(22);
// should have written a submitted time

@ -33,7 +33,8 @@ function transformState(state) {
delete frequentRpcListDetail[index].chainId;
}
});
newState.PreferencesController.frequentRpcListDetail = frequentRpcListDetail;
newState.PreferencesController.frequentRpcListDetail =
frequentRpcListDetail;
}
}
if (state.NetworkController) {

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

Loading…
Cancel
Save