1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Merge remote-tracking branch 'origin/develop' into master-sync

This commit is contained in:
ryanml 2021-06-15 14:17:26 -07:00
commit c60313eeac
279 changed files with 5648 additions and 3421 deletions

View File

@ -343,7 +343,7 @@ jobs:
command: |
if .circleci/scripts/test-run-e2e.sh
then
yarn test:e2e:chrome
yarn test:e2e:chrome --retries 2
fi
no_output_timeout: 20m
- store_artifacts:
@ -370,7 +370,7 @@ jobs:
command: |
if .circleci/scripts/test-run-e2e.sh
then
yarn test:e2e:chrome:metrics
yarn test:e2e:chrome:metrics --retries 2
fi
no_output_timeout: 20m
- store_artifacts:
@ -397,7 +397,7 @@ jobs:
command: |
if .circleci/scripts/test-run-e2e.sh
then
yarn test:e2e:firefox
yarn test:e2e:firefox --retries 2
fi
no_output_timeout: 20m
- store_artifacts:
@ -424,7 +424,7 @@ jobs:
command: |
if .circleci/scripts/test-run-e2e.sh
then
yarn test:e2e:firefox:metrics
yarn test:e2e:firefox:metrics --retries 2
fi
no_output_timeout: 20m
- store_artifacts:

View File

@ -1,7 +1,7 @@
blank_issues_enabled: false
contact_links:
- name: Request a new feature
url: https://metamask.zendesk.com/hc/en-us/community/topics/360000682552-Feature-Requests
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

View File

@ -9,6 +9,9 @@ jobs:
CLABot:
if: github.event_name == 'pull_request_target' || contains(github.event.comment.html_url, '/pull/')
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
steps:
- name: "CLA Signature Bot"
uses: MetaMask/cla-signature-bot@v3.0.2

View File

@ -0,0 +1,56 @@
export const currentNetworkTxListSample = {
"id": 7900715443136469,
"time": 1621395091737,
"status": "unapproved",
"metamaskNetworkId": "1337",
"chainId": "0x539",
"loadingDefaults": false,
"txParams": {
"from": "0x90f79bf6eb2c4f870365e785982e1f101e93b906",
"to": "0x057ef64e23666f000b34ae31332854acbd1c8544",
"value": "0x0",
"data": "0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170",
"gas": "0xea60",
"gasPrice": "0x4a817c800"
},
"origin": "https://metamask.github.io",
"type": "approve",
"history": [
{
"id": 7900715443136469,
"time": 1621395091737,
"status": "unapproved",
"metamaskNetworkId": "1337",
"chainId": "0x539",
"loadingDefaults": true,
"txParams": {
"from": "0x90f79bf6eb2c4f870365e785982e1f101e93b906",
"to": "0x057ef64e23666f000b34ae31332854acbd1c8544",
"value": "0x0",
"data": "0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170",
"gas": "0xea60",
"gasPrice": "0x4a817c800"
},
"origin": "https://metamask.github.io",
"type": "approve"
},
[
{
"op": "replace",
"path": "/loadingDefaults",
"value": false,
"note": "Added new unapproved transaction.",
"timestamp": 1621395091742
}
]
]
}
export const domainMetadata = {
"https://metamask.github.io": {
"name": "E2E Test Dapp",
"icon": "https://metamask.github.io/test-dapp/metamask-fox.svg",
"lastUpdated": 1620723443380,
"host": "metamask.github.io"
}
}

24
.storybook/metametrics.js Normal file
View File

@ -0,0 +1,24 @@
import React from 'react';
import {
MetaMetricsProvider,
LegacyMetaMetricsProvider,
} from '../ui/contexts/metametrics';
import {
MetaMetricsProvider as NewMetaMetricsProvider,
LegacyMetaMetricsProvider as NewLegacyMetaMetricsProvider,
} from '../ui/contexts/metametrics.new';
const MetaMetricsProviderStorybook = (props) =>
(
<MetaMetricsProvider>
<LegacyMetaMetricsProvider>
<NewMetaMetricsProvider>
<NewLegacyMetaMetricsProvider>
{props.children}
</NewLegacyMetaMetricsProvider>
</NewMetaMetricsProvider>
</LegacyMetaMetricsProvider>
</MetaMetricsProvider>
);
export default MetaMetricsProviderStorybook

View File

@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import { addDecorator, addParameters } from '@storybook/react';
import { useGlobals } from '@storybook/api';
import { action } from '@storybook/addon-actions';
import { withKnobs } from '@storybook/addon-knobs';
import { Provider } from 'react-redux';
import configureStore from '../ui/store/store';
@ -8,7 +8,11 @@ import '../ui/css/index.scss';
import localeList from '../app/_locales/index.json';
import * as allLocales from './locales';
import { I18nProvider, LegacyI18nProvider } from './i18n';
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'
addParameters({
backgrounds: {
@ -41,22 +45,36 @@ const styles = {
alignItems: 'center',
};
const store = configureStore(testData);
export const store = configureStore(testData);
const history = createBrowserHistory();
const proxiedBackground = new Proxy({}, {
get(_, method) {
return function() {
action(`Background call: ${method}`)()
return new Promise(() => {})
}
}
})
_setBackgroundConnection(proxiedBackground)
const metamaskDecorator = (story, context) => {
const currentLocale = context.globals.locale;
const current = allLocales[currentLocale];
return (
<Provider store={store}>
<I18nProvider
currentLocale={currentLocale}
current={current}
en={allLocales.en}
>
<LegacyI18nProvider>
<div style={styles}>{story()}</div>
</LegacyI18nProvider>
</I18nProvider>
<Router history={history}>
<MetaMetricsProviderStorybook>
<I18nProvider
currentLocale={currentLocale}
current={current}
en={allLocales.en}
>
<LegacyI18nProvider>
<div style={styles}>{story()}</div>
</LegacyI18nProvider>
</I18nProvider>
</MetaMetricsProviderStorybook>
</Router>
</Provider>
);
};

View File

@ -1,217 +1,782 @@
import { TRANSACTION_STATUSES } from '../shared/constants/transaction';
const state = {
metamask: {
isInitialized: true,
isUnlocked: true,
featureFlags: { sendHexData: true },
identities: {
'0xfdea65c8e26263f6d9a1b5de9555d2931a33b825': {
address: '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825',
name: 'Send Account 1',
"invalidCustomNetwork": {
"state": "CLOSED",
"networkName": ""
},
"unconnectedAccount": {
"state": "CLOSED"
},
"activeTab": {},
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"isAccountMenuOpen": false,
"rpcUrl": "https://rawtestrpc.metamask.io/",
"identities": {
"0x983211ce699ea5ab57cc528086154b6db1ad8e55": {
"name": "Account 1",
"address": "0x983211ce699ea5ab57cc528086154b6db1ad8e55"
},
'0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb': {
address: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
name: 'Send Account 2',
},
'0x2f8d4a878cfa04a6e60d46362f5644deab66572d': {
address: '0x2f8d4a878cfa04a6e60d46362f5644deab66572d',
name: 'Send Account 3',
},
'0xd85a4b6a394794842887b8284293d69163007bbb': {
address: '0xd85a4b6a394794842887b8284293d69163007bbb',
name: 'Send Account 4',
"0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e": {
"name": "Account 2",
"address": "0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e"
},
"0x9d0ba4ddac06032527b140912ec808ab9451b788": {
"name": "Account 3",
"address": "0x9d0ba4ddac06032527b140912ec808ab9451b788"
}
},
cachedBalances: {},
currentBlockGasLimit: '0x4c1878',
currentCurrency: 'USD',
conversionRate: 1200.88200327,
conversionDate: 1489013762,
nativeCurrency: 'ETH',
frequentRpcList: [],
network: '3',
provider: {
type: 'ropsten',
chainId: '0x3',
},
accounts: {
'0xfdea65c8e26263f6d9a1b5de9555d2931a33b825': {
code: '0x',
balance: '0x47c9d71831c76efe',
nonce: '0x1b',
address: '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825',
},
'0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb': {
code: '0x',
balance: '0x37452b1315889f80',
nonce: '0xa',
address: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
},
'0x2f8d4a878cfa04a6e60d46362f5644deab66572d': {
code: '0x',
balance: '0x30c9d71831c76efe',
nonce: '0x1c',
address: '0x2f8d4a878cfa04a6e60d46362f5644deab66572d',
},
'0xd85a4b6a394794842887b8284293d69163007bbb': {
code: '0x',
balance: '0x0',
nonce: '0x0',
address: '0xd85a4b6a394794842887b8284293d69163007bbb',
},
},
addressBook: {
'0x3': {
'0x06195827297c7a80a443b6894d3bdb8824b43896': {
address: '0x06195827297c7a80a443b6894d3bdb8824b43896',
name: 'Address Book Account 1',
chainId: '0x3',
"unapprovedTxs": {
"7786962153682822": {
"id": 7786962153682822,
"time": 1620710815484,
"status": "unapproved",
"metamaskNetworkId": "3",
"chainId": "0x3",
"loadingDefaults": false,
"txParams": {
"from": "0x64a845a5b02460acf8a3d84503b0d68d028b4bb4",
"to": "0xad6d458402f60fd3bd25163575031acdce07538d",
"value": "0x0",
"data": "0xa9059cbb000000000000000000000000b19ac54efa18cc3a14a5b821bfec73d284bf0c5e0000000000000000000000000000000000000000000000003782dace9d900000",
"gas": "0xcb28",
"gasPrice": "0x77359400"
},
},
"type": "standard",
"origin": "metamask",
"transactionCategory": "transfer",
"history": [
{
"id": 7786962153682822,
"time": 1620710815484,
"status": "unapproved",
"metamaskNetworkId": "3",
"chainId": "0x3",
"loadingDefaults": true,
"txParams": {
"from": "0x64a845a5b02460acf8a3d84503b0d68d028b4bb4",
"to": "0xad6d458402f60fd3bd25163575031acdce07538d",
"value": "0x0",
"data": "0xa9059cbb000000000000000000000000b19ac54efa18cc3a14a5b821bfec73d284bf0c5e0000000000000000000000000000000000000000000000003782dace9d900000",
"gas": "0xcb28",
"gasPrice": "0x77359400"
},
"type": "standard",
"origin": "metamask",
"transactionCategory": "transfer"
},
[
{
"op": "replace",
"path": "/loadingDefaults",
"value": false,
"note": "Added new unapproved transaction.",
"timestamp": 1620710815497
}
]
]
}
},
tokens: [
"frequentRpcList": [],
"addressBook": {
"undefined": {
"0": {
"address": "0x39a4e4Af7cCB654dB9500F258c64781c8FbD39F0",
"name": "",
"isEns": false
}
}
},
"contractExchangeRates": {
"0xad6d458402f60fd3bd25163575031acdce07538d": 0
},
"tokens": [
{
address: '0x1a195821297c7a80a433b6894d3bdb8824b43896',
decimals: 18,
symbol: 'ABC',
},
{
address: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
decimals: 4,
symbol: 'DEF',
},
{
address: '0xa42084c8d1d9a2198631988579bb36b48433a72b',
decimals: 18,
symbol: 'GHI',
},
"address": "0xad6d458402f60fd3bd25163575031acdce07538d",
"symbol": "DAI",
"decimals": 18
}
],
transactions: {},
currentNetworkTxList: [
{
id: 'mockTokenTx1',
txParams: {
to: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
from: '0xd85a4b6a394794842887b8284293d69163007bbb',
},
time: 1700000000000,
},
{
id: 'mockTokenTx2',
txParams: {
to: '0xafaketokenaddress',
from: '0xd85a4b6a394794842887b8284293d69163007bbb',
},
time: 1600000000000,
},
{
id: 'mockTokenTx3',
txParams: {
to: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
from: '0xd85a4b6a394794842887b8284293d69163007bbb',
},
time: 1500000000000,
},
{
id: 'mockEthTx1',
txParams: {
to: '0xd85a4b6a394794842887b8284293d69163007bbb',
from: '0xd85a4b6a394794842887b8284293d69163007bbb',
},
time: 1400000000000,
},
],
unapprovedMsgs: {
'0xabc': { id: 'unapprovedMessage1', time: 1650000000000 },
'0xdef': { id: 'unapprovedMessage2', time: 1550000000000 },
'0xghi': { id: 'unapprovedMessage3', time: 1450000000000 },
"pendingTokens": {},
"customNonceValue": "",
"send": {
"gasLimit": "0xcb28",
"gasPrice": null,
"gasTotal": null,
"tokenBalance": "8.7a73149c048545a3fe58",
"from": "",
"to": "0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e",
"amount": "3782dace9d900000",
"memo": "",
"errors": {},
"maxModeOn": false,
"editingTransactionId": null,
"toNickname": "Account 2",
"ensResolution": null,
"ensResolutionError": "",
"token": {
"address": "0xad6d458402f60fd3bd25163575031acdce07538d",
"symbol": "DAI",
"decimals": 18
}
},
unapprovedMsgCount: 0,
unapprovedPersonalMsgs: {},
unapprovedPersonalMsgCount: 0,
unapprovedDecryptMsgs: {},
unapprovedDecryptMsgCount: 0,
unapprovedEncryptionPublicKeyMsgs: {},
unapprovedEncryptionPublicKeyMsgCount: 0,
keyringTypes: ['Simple Key Pair', 'HD Key Tree'],
keyrings: [
"useBlockie": false,
"featureFlags": {},
"welcomeScreenSeen": false,
"currentLocale": "en",
"preferences": {
"useNativeCurrencyAsPrimaryCurrency": true
},
"firstTimeFlowType": "create",
"completedOnboarding": true,
"knownMethodData": {
"0x60806040": {
"name": "Approve Tokens"
},
"0x095ea7b3": {
"name": "Approve Tokens"
}
},
"participateInMetaMetrics": true,
"metaMetricsSendCount": 2,
"nextNonce": 71,
"connectedStatusPopoverHasBeenShown": true,
"swapsWelcomeMessageHasBeenShown": true,
"defaultHomeActiveTabName": "Assets",
"provider": {
"type": "ropsten",
"ticker": "ETH",
"nickname": "",
"rpcUrl": "",
"chainId": "0x3"
},
"previousProviderStore": {
"type": "ropsten",
"ticker": "ETH",
"nickname": "",
"rpcUrl": "",
"chainId": "0x3"
},
"network": "3",
"accounts": {
"0x983211ce699ea5ab57cc528086154b6db1ad8e55": {
"address": "0x983211ce699ea5ab57cc528086154b6db1ad8e55",
"balance": "0x176e5b6f173ebe66"
},
"0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e": {
"address": "0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e",
"balance": "0x2d3142f5000"
},
"0x9d0ba4ddac06032527b140912ec808ab9451b788": {
"address": "0x9d0ba4ddac06032527b140912ec808ab9451b788",
"balance": "0x15f6f0b9d4f8d000"
}
},
"currentBlockGasLimit": "0x793af4",
"currentNetworkTxList": [
],
"cachedBalances": {
"1": {
"0x64a845a5b02460acf8a3d84503b0d68d028b4bb4": "0x0",
"0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e": "0xcaf5317161f400",
"0x9d0ba4ddac06032527b140912ec808ab9451b788": "0x0"
},
"3": {
"0x64a845a5b02460acf8a3d84503b0d68d028b4bb4": "0x18d289d450bace66",
"0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e": "0x2d3142f5000",
"0x9d0ba4ddac06032527b140912ec808ab9451b788": "0x15f6f0b9d4f8d000"
},
"0x3": {
"0x64a845a5b02460acf8a3d84503b0d68d028b4bb4": "0x176e5b6f173ebe66",
"0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e": "0x2d3142f5000",
"0x9d0ba4ddac06032527b140912ec808ab9451b788": "0x15f6f0b9d4f8d000"
}
},
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},
"unapprovedPersonalMsgCount": 0,
"unapprovedDecryptMsgs": {},
"unapprovedDecryptMsgCount": 0,
"unapprovedEncryptionPublicKeyMsgs": {},
"unapprovedEncryptionPublicKeyMsgCount": 0,
"unapprovedTypedMessages": {},
"unapprovedTypedMessagesCount": 0,
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree",
"Trezor Hardware",
"Ledger Hardware"
],
"keyrings": [
{
type: 'HD Key Tree',
accounts: [
'fdea65c8e26263f6d9a1b5de9555d2931a33b825',
'c5b8dbac4c1d3f152cdeb400e2313f309c410acb',
'2f8d4a878cfa04a6e60d46362f5644deab66572d',
"type": "HD Key Tree",
"accounts": [
"0x64a845a5b02460acf8a3d84503b0d68d028b4bb4",
"0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e",
"0x9d0ba4ddac06032527b140912ec808ab9451b788"
]
}
],
"frequentRpcListDetail": [
{
"rpcUrl": "http://localhost:8545",
"chainId": "0x539",
"ticker": "ETH",
"nickname": "Localhost 8545",
"rpcPrefs": {}
}
],
"accountTokens": {
"0x64a845a5b02460acf8a3d84503b0d68d028b4bb4": {
"0x1": [
{
"address": "0x6b175474e89094c44da98b954eedeac495271d0f",
"symbol": "DAI",
"decimals": 18
},
{
"address": "0x0d8775f648430679a709e98d2b0cb6250d2887ef",
"symbol": "BAT",
"decimals": 18
}
],
"0x3": [
{
"address": "0xad6d458402f60fd3bd25163575031acdce07538d",
"symbol": "DAI",
"decimals": 18
}
]
},
"0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e": {},
"0x9d0ba4ddac06032527b140912ec808ab9451b788": {}
},
"accountHiddenTokens": {
"0x64a845a5b02460acf8a3d84503b0d68d028b4bb4": {
"0x3": []
}
},
"assetImages": {
"0xad6d458402f60fd3bd25163575031acdce07538d": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaD6D458402F60fD3Bd25163575031ACDce07538D/logo.png"
},
"hiddenTokens": [],
"suggestedTokens": {},
"useNonceField": false,
"usePhishDetect": true,
"lostIdentities": {},
"forgottenPassword": false,
"ipfsGateway": "dweb.link",
"infuraBlocked": false,
"migratedPrivacyMode": false,
"selectedAddress": "0x64a845a5b02460acf8a3d84503b0d68d028b4bb4",
"metaMetricsId": "0xc2377d11fec1c3b7dd88c4854240ee5e3ed0d9f63b00456d98d80320337b827f",
"conversionDate": 1620710825.03,
"conversionRate": 3910.28,
"currentCurrency": "usd",
"nativeCurrency": "ETH",
"usdConversionRate": 3910.28,
"ticker": "ETH",
"alertEnabledness": {
"unconnectedAccount": true,
"web3ShimUsage": true
},
"unconnectedAccountAlertShownOrigins": {},
"web3ShimUsageOrigins": {},
"seedPhraseBackedUp": null,
"onboardingTabs": {},
"incomingTransactions": {
"0x2de9256a7c604586f7ecfd87ae9509851e217f588f9f85feed793c54ed2ce0aa": {
"blockNumber": "8888976",
"id": 4678200543090532,
"metamaskNetworkId": "1",
"status": "confirmed",
"time": 1573114896000,
"txParams": {
"from": "0x3f1b52850109023775d238c7ed5d5e7161041fd1",
"gas": "0x5208",
"gasPrice": "0x124101100",
"nonce": "0x35",
"to": "0x045c619e4d29bba3b92769508831b681b83d6a96",
"value": "0xbca9bce4d98ca3"
},
"hash": "0x2de9256a7c604586f7ecfd87ae9509851e217f588f9f85feed793c54ed2ce0aa",
"transactionCategory": "incoming"
},
"0x320a1fd769373578f78570e5d8f56e89bc7bce9657bb5f4c12d8fe790d471bfd": {
"blockNumber": "9453174",
"id": 4678200543090535,
"metamaskNetworkId": "1",
"status": "confirmed",
"time": 1581312411000,
"txParams": {
"from": "0xa17bd07d6d38cb9e37b29f7659a4b1047701e969",
"gas": "0xc350",
"gasPrice": "0x1a13b8600",
"nonce": "0x0",
"to": "0x045c619e4d29bba3b92769508831b681b83d6a96",
"value": "0xcdb08ab4254000"
},
"hash": "0x320a1fd769373578f78570e5d8f56e89bc7bce9657bb5f4c12d8fe790d471bfd",
"transactionCategory": "incoming"
},
"0x8add6c1ea089a8de9b15fa2056b1875360f17916755c88ace9e5092b7a4b1239": {
"blockNumber": "10892417",
"id": 4678200543090542,
"metamaskNetworkId": "1",
"status": "confirmed",
"time": 1600515224000,
"txParams": {
"from": "0x0681d8db095565fe8a346fa0277bffde9c0edbbf",
"gas": "0x5208",
"gasPrice": "0x1d1a94a200",
"nonce": "0x2bb8a5",
"to": "0x045c619e4d29bba3b92769508831b681b83d6a96",
"value": "0xe6ed27d6668000"
},
"hash": "0x8add6c1ea089a8de9b15fa2056b1875360f17916755c88ace9e5092b7a4b1239",
"transactionCategory": "incoming"
},
"0x50be62ab1cabd03ff104c602c11fdef7a50f3d73c55006d5583ba97950ab1144": {
"blockNumber": "10902987",
"id": 4678200543090545,
"metamaskNetworkId": "1",
"status": "confirmed",
"time": 1600654021000,
"txParams": {
"from": "0x64a845a5b02460acf8a3d84503b0d68d028b4bb4",
"gas": "0x5208",
"gasPrice": "0x147d357000",
"nonce": "0xf",
"to": "0x045c619e4d29bba3b92769508831b681b83d6a96",
"value": "0x63eb89da4ed00000"
},
"hash": "0x50be62ab1cabd03ff104c602c11fdef7a50f3d73c55006d5583ba97950ab1144",
"transactionCategory": "incoming"
}
},
"incomingTxLastFetchedBlocksByNetwork": {
"ropsten": 8872820,
"rinkeby": null,
"kovan": null,
"goerli": null,
"mainnet": 10902989
},
"permissionsRequests": [],
"permissionsDescriptions": {},
"domains": {
"https://app.uniswap.org": {
"permissions": [
{
"@context": [
"https://github.com/MetaMask/rpc-cap"
],
"invoker": "https://app.uniswap.org",
"parentCapability": "eth_accounts",
"id": "a7342e4b-beae-4525-a36c-c0635fd03359",
"date": 1620710693178,
"caveats": [
{
"type": "limitResponseLength",
"value": 1,
"name": "primaryAccountOnly"
},
{
"type": "filterResponse",
"value": [
"0x64a845a5b02460acf8a3d84503b0d68d028b4bb4"
],
"name": "exposedAccounts"
}
]
}
]
}
},
"permissionsLog": [
{
"id": 522690215,
"method": "eth_accounts",
"methodType": "restricted",
"origin": "https://metamask.io",
"request": {
"method": "eth_accounts",
"params": [],
"jsonrpc": "2.0",
"id": 522690215,
"origin": "https://metamask.io",
"tabId": 5
},
"requestTime": 1602643170686,
"response": {
"id": 522690215,
"jsonrpc": "2.0",
"result": []
},
"responseTime": 1602643170688,
"success": true
},
{
type: 'Simple Key Pair',
accounts: ['0xd85a4b6a394794842887b8284293d69163007bbb'],
},
],
selectedAddress: '0xd85a4b6a394794842887b8284293d69163007bbb',
send: {
gasLimit: '0xFFFF',
gasPrice: '0xaa',
gasTotal: '0xb451dc41b578',
tokenBalance: 3434,
from: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
to: '0x987fedabc',
amount: '0x080',
memo: '',
errors: {
someError: null,
},
maxModeOn: false,
editingTransactionId: 97531,
},
unapprovedTxs: {
4768706228115573: {
id: 4768706228115573,
time: 1487363153561,
status: TRANSACTION_STATUSES.UNAPPROVED,
gasMultiplier: 1,
metamaskNetworkId: '3',
txParams: {
from: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
to: '0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761',
value: '0xde0b6b3a7640000',
metamaskId: 4768706228115573,
metamaskNetworkId: '3',
gas: '0x5209',
"id": 1620464600,
"method": "eth_accounts",
"methodType": "restricted",
"origin": "https://widget.getacute.io",
"request": {
"method": "eth_accounts",
"params": [],
"jsonrpc": "2.0",
"id": 1620464600,
"origin": "https://widget.getacute.io",
"tabId": 5
},
txFee: '17e0186e60800',
txValue: 'de0b6b3a7640000',
maxCost: 'de234b52e4a0800',
gasPrice: '4a817c800',
"requestTime": 1602643172935,
"response": {
"id": 1620464600,
"jsonrpc": "2.0",
"result": []
},
"responseTime": 1602643172935,
"success": true
},
{
"id": 4279100021,
"method": "eth_accounts",
"methodType": "restricted",
"origin": "https://app.uniswap.org",
"request": {
"method": "eth_accounts",
"jsonrpc": "2.0",
"id": 4279100021,
"origin": "https://app.uniswap.org",
"tabId": 5
},
"requestTime": 1620710669962,
"response": {
"id": 4279100021,
"jsonrpc": "2.0",
"result": []
},
"responseTime": 1620710669963,
"success": true
},
{
"id": 4279100022,
"method": "eth_requestAccounts",
"methodType": "restricted",
"origin": "https://app.uniswap.org",
"request": {
"method": "eth_requestAccounts",
"jsonrpc": "2.0",
"id": 4279100022,
"origin": "https://app.uniswap.org",
"tabId": 5
},
"requestTime": 1620710686872,
"response": {
"id": 4279100022,
"jsonrpc": "2.0",
"result": [
"0x64a845a5b02460acf8a3d84503b0d68d028b4bb4"
]
},
"responseTime": 1620710693187,
"success": true
},
{
"id": 4279100023,
"method": "eth_requestAccounts",
"methodType": "restricted",
"origin": "https://app.uniswap.org",
"request": {
"method": "eth_requestAccounts",
"jsonrpc": "2.0",
"id": 4279100023,
"origin": "https://app.uniswap.org",
"tabId": 5
},
"requestTime": 1620710693204,
"response": {
"id": 4279100023,
"jsonrpc": "2.0",
"result": [
"0x64a845a5b02460acf8a3d84503b0d68d028b4bb4"
]
},
"responseTime": 1620710693213,
"success": true
},
{
"id": 4279100034,
"method": "eth_accounts",
"methodType": "restricted",
"origin": "https://app.uniswap.org",
"request": {
"method": "eth_accounts",
"params": [],
"jsonrpc": "2.0",
"id": 4279100034,
"origin": "https://app.uniswap.org",
"tabId": 5
},
"requestTime": 1620710712072,
"response": {
"id": 4279100034,
"jsonrpc": "2.0",
"result": [
"0x64a845a5b02460acf8a3d84503b0d68d028b4bb4"
]
},
"responseTime": 1620710712075,
"success": true
}
],
"permissionsHistory": {
"https://app.uniswap.org": {
"eth_accounts": {
"lastApproved": 1620710693213,
"accounts": {
"0x64a845a5b02460acf8a3d84503b0d68d028b4bb4": 1620710693213
}
}
}
},
currentLocale: 'en',
"domainMetadata": {
"https://metamask.github.io": {
"name": "E2E Test Dapp",
"icon": "https://metamask.github.io/test-dapp/metamask-fox.svg",
"lastUpdated": 1620723443380,
"host": "metamask.github.io"
}
},
"threeBoxSyncingAllowed": false,
"showRestorePrompt": true,
"threeBoxLastUpdated": 0,
"threeBoxAddress": null,
"threeBoxSynced": false,
"threeBoxDisabled": false,
"swapsState": {
"quotes": {},
"fetchParams": null,
"tokens": null,
"tradeTxId": null,
"approveTxId": null,
"quotesLastFetched": null,
"customMaxGas": "",
"customGasPrice": null,
"selectedAggId": null,
"customApproveTxData": "",
"errorKey": "",
"topAggId": null,
"routeState": "",
"swapsFeatureIsLive": false,
"swapsQuoteRefreshTime": 60000
},
"ensResolutionsByAddress": {},
"pendingApprovals": {},
"pendingApprovalCount": 0
},
appState: {
menuOpen: false,
currentView: {
name: 'accountDetail',
detailView: null,
context: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
"appState": {
"shouldClose": false,
"menuOpen": false,
"modal": {
"open": false,
"modalState": {
"name": null,
"props": {}
},
"previousModalState": {
"name": null
}
},
accountDetail: {
subview: 'transactions',
"sidebar": {
"isOpen": false,
"transitionName": "",
"type": "",
"props": {}
},
modal: {
modalState: {},
previousModalState: {},
"alertOpen": false,
"alertMessage": null,
"qrCodeData": null,
"networkDropdownOpen": false,
"accountDetail": {
"subview": "transactions"
},
isLoading: false,
warning: null,
scrollToBottom: false,
forgottenPassword: null,
"isLoading": false,
"warning": null,
"buyView": {},
"isMouseUser": true,
"gasIsLoading": false,
"defaultHdPaths": {
"trezor": "m/44'/60'/0'/0",
"ledger": "m/44'/60'/0'/0/0"
},
"networksTabSelectedRpcUrl": "",
"networksTabIsInAddMode": false,
"loadingMethodData": false,
"show3BoxModalAfterImport": false,
"threeBoxLastUpdated": null,
"requestAccountTabs": {},
"openMetaMaskTabs": {},
"currentWindowTab": {}
},
send: {
fromDropdownOpen: false,
toDropdownOpen: false,
errors: { someError: null },
"history": {
"mostRecentOverviewPage": "/"
},
};
"send": {
"toDropdownOpen": false,
"gasButtonGroupShown": true,
"errors": {}
},
"confirmTransaction": {
"txData": {
"id": 3111025347726181,
"time": 1620723786838,
"status": "unapproved",
"metamaskNetworkId": "3",
"chainId": "0x3",
"loadingDefaults": false,
"txParams": {
"from": "0x983211ce699ea5ab57cc528086154b6db1ad8e55",
"to": "0xad6d458402f60fd3bd25163575031acdce07538d",
"value": "0x0",
"data": "0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170",
"gas": "0xea60",
"gasPrice": "0x4a817c800"
},
"type": "standard",
"origin": "https://metamask.github.io",
"transactionCategory": "approve",
"history": [
{
"id": 3111025347726181,
"time": 1620723786838,
"status": "unapproved",
"metamaskNetworkId": "3",
"chainId": "0x3",
"loadingDefaults": true,
"txParams": {
"from": "0x983211ce699ea5ab57cc528086154b6db1ad8e55",
"to": "0xad6d458402f60fd3bd25163575031acdce07538d",
"value": "0x0",
"data": "0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170",
"gas": "0xea60",
"gasPrice": "0x4a817c800"
},
"type": "standard",
"origin": "https://metamask.github.io",
"transactionCategory": "approve"
},
[
{
"op": "replace",
"path": "/loadingDefaults",
"value": false,
"note": "Added new unapproved transaction.",
"timestamp": 1620723786844
}
]
]
},
"tokenData": {
"args": [
"0x9bc5baF874d2DA8D216aE9f137804184EE5AfEF4",
{
"type": "BigNumber",
"hex": "0x011170"
}
],
"functionFragment": {
"type": "function",
"name": "approve",
"constant": false,
"inputs": [
{
"name": "_spender",
"type": "address",
"indexed": null,
"components": null,
"arrayLength": null,
"arrayChildren": null,
"baseType": "address",
"_isParamType": true
},
{
"name": "_value",
"type": "uint256",
"indexed": null,
"components": null,
"arrayLength": null,
"arrayChildren": null,
"baseType": "uint256",
"_isParamType": true
}
],
"outputs": [
{
"name": "success",
"type": "bool",
"indexed": null,
"components": null,
"arrayLength": null,
"arrayChildren": null,
"baseType": "bool",
"_isParamType": true
}
],
"payable": false,
"stateMutability": "nonpayable",
"gas": null,
"_isFragment": true
},
"name": "approve",
"signature": "approve(address,uint256)",
"sighash": "0x095ea7b3",
"value": {
"type": "BigNumber",
"hex": "0x00"
}
},
"fiatTransactionAmount": "0",
"fiatTransactionFee": "4.72",
"fiatTransactionTotal": "4.72",
"ethTransactionAmount": "0",
"ethTransactionFee": "0.0012",
"ethTransactionTotal": "0.0012",
"hexTransactionAmount": "0x0",
"hexTransactionFee": "0x44364c5bb0000",
"hexTransactionTotal": "0x44364c5bb0000",
"nonce": ""
},
"swaps": {
"aggregatorMetadata": null,
"approveTxId": null,
"balanceError": false,
"fetchingQuotes": false,
"fromToken": null,
"quotesFetchStartTime": null,
"topAssets": {},
"toToken": null,
"customGas": {
"price": null,
"limit": null,
"loading": "INITIAL",
"priceEstimates": {},
"fallBackPrice": null
}
},
"gas": {
"customData": {
"price": null,
"limit": "0xcb28"
},
"basicEstimates": {
"average": 2
},
"basicEstimateIsLoading": false
}
}
export default state;

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ Hey! We are hiring JavaScript Engineers! [Apply here](https://boards.greenhouse.
You can find the latest version of MetaMask on [our official website](https://metamask.io/). For help using MetaMask, visit our [User Support Site](https://metamask.zendesk.com/hc/en-us).
For [general questions](https://metamask.zendesk.com/hc/en-us/community/topics/360000682532-General), [feature requests](https://metamask.zendesk.com/hc/en-us/community/topics/360000682552-Feature-Requests-Ideas), or [developer questions](https://metamask.zendesk.com/hc/en-us/community/topics/360001751291-Developer-Questions), visit our [Community Forum](https://metamask.zendesk.com/hc/en-us/community/topics).
For [general questions](https://community.metamask.io/c/learn/26), [feature requests](https://community.metamask.io/c/feature-requests-ideas/13), or [developer questions](https://community.metamask.io/c/developer-questions/11), visit our [Community Forum](https://community.metamask.io/).
MetaMask supports Firefox, Google Chrome, and Chromium-based browsers. We recommend using the latest available browser version.

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "ማሰሺያዎት አልተደገፈም..."
},
"builtInCalifornia": {
"message": "MetaMask ካሊፎርኒያ ውስጥ ተዘጋጅቶ የተገነባ ነው።"
},
"buyWithWyre": {
"message": "ETH በ Wyre ይግዙ"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "متصفحك غير مدعوم..."
},
"builtInCalifornia": {
"message": "تم تصميم وإنشاء MetaMask في ولاية كاليفورنيا."
},
"buyWithWyre": {
"message": "قم بشراء عملة إيثير بواسطة Wyre"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Браузърът ви не се поддържа ..."
},
"builtInCalifornia": {
"message": "MetaMask е проектиран и създаден в Калифорния."
},
"buyWithWyre": {
"message": "Купете ETH с Wyre"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "আপনার ব্রাউজার সমর্থিত নয়..."
},
"builtInCalifornia": {
"message": "MetaMask ক্যালিফোর্নিয়াতে ডিজাইন করা এবং নির্মিত।"
},
"buyWithWyre": {
"message": "Wyre দিয়ে ETH ক্রয় করুন"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "El teu navegador no és suportat..."
},
"builtInCalifornia": {
"message": "MetaMask ha estat dissenyat i desenvolupat a Califòrnia."
},
"buyWithWyre": {
"message": "Compra ETH amb Wyre"
},

View File

@ -46,9 +46,6 @@
"blockiesIdenticon": {
"message": "Použít Blockies Identicon"
},
"builtInCalifornia": {
"message": "MetaMask je navržen a vytvořen v Kalifornii."
},
"cancel": {
"message": "Zrušit"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Din browser er ikke understøttet..."
},
"builtInCalifornia": {
"message": "MetaMask er designet og bygget i Californien."
},
"buyWithWyre": {
"message": "Køb ETH med Wyre"
},

View File

@ -140,9 +140,6 @@
"browserNotSupported": {
"message": "Ihr Browser wird nicht unterstützt …"
},
"builtInCalifornia": {
"message": "MetaMask wurde in Kalifornien entwickelt und gebaut."
},
"buyWithWyre": {
"message": "ETH mit Wyre kaufen"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Το Πρόγραμμα Περιήγησής σας δεν υποστηρίζεται..."
},
"builtInCalifornia": {
"message": "Το MetaMask έχει σχεδιαστεί και αναπτυχθεί στην Καλιφόρνια."
},
"buyWithWyre": {
"message": "Αγοράστε ETH με το Wyre"
},

View File

@ -52,6 +52,10 @@
"addContact": {
"message": "Add contact"
},
"addCustomTokenByContractAddress": {
"message": "Cant find a token? You can manually add any token by pasting its address. Token contract addresses can be found on $1.",
"description": "$1 is a blockchain explorer for a specific network, e.g. Etherscan for Ethereum"
},
"addEthereumChainConfirmationDescription": {
"message": "This will allow this network to be used within MetaMask."
},
@ -93,6 +97,9 @@
"addTokens": {
"message": "Add Tokens"
},
"addressBookIcon": {
"message": "Address book icon"
},
"advanced": {
"message": "Advanced"
},
@ -146,6 +153,9 @@
"amount": {
"message": "Amount"
},
"amountGasFee": {
"message": "Amount + Gas Fee"
},
"amountWithColon": {
"message": "Amount:"
},
@ -249,11 +259,11 @@
"browserNotSupported": {
"message": "Your Browser is not supported..."
},
"builContactList": {
"buildContactList": {
"message": "Build your contact list"
},
"builtInCalifornia": {
"message": "MetaMask is designed and built in California."
"builtAroundTheWorld": {
"message": "MetaMask is designed and built around the world."
},
"buy": {
"message": "Buy"
@ -285,6 +295,9 @@
"chainIdDefinition": {
"message": "The chain ID used to sign transactions for this network."
},
"chainIdExistsErrorMsg": {
"message": "This Chain ID is currently used by the $1 network."
},
"chromeRequiredForHardwareWallets": {
"message": "You need to use MetaMask on Google Chrome in order to connect to your Hardware Wallet."
},
@ -410,6 +423,9 @@
"continueToWyre": {
"message": "Continue to Wyre"
},
"contract": {
"message": "Contract"
},
"contractAddressError": {
"message": "You are sending tokens to the token's contract address. This may result in the loss of these tokens."
},
@ -482,6 +498,9 @@
"customToken": {
"message": "Custom Token"
},
"data": {
"message": "Data"
},
"dataBackupFoundInfo": {
"message": "Some of your account data was backed up during a previous installation of MetaMask. This could include your settings, contacts, and tokens. Would you like to restore this data now?"
},
@ -767,6 +786,9 @@
"functionType": {
"message": "Function Type"
},
"gasFee": {
"message": "Gas Fee"
},
"gasLimit": {
"message": "Gas Limit"
},
@ -893,6 +915,12 @@
"message": "or $1",
"description": "$1 represents the text from `importAccountLinkText` as a link"
},
"importTokenQuestion": {
"message": "Import token?"
},
"importTokenWarning": {
"message": "Anyone can create a token with any name, including fake versions of existing tokens. Add and trade at your own risk!"
},
"importWallet": {
"message": "Import wallet"
},
@ -1042,6 +1070,9 @@
"mainnet": {
"message": "Ethereum Mainnet"
},
"makeAnotherSwap": {
"message": "Create a new swap"
},
"max": {
"message": "Max"
},
@ -1336,6 +1367,9 @@
"onlyConnectTrust": {
"message": "Only connect with sites you trust."
},
"optional": {
"message": "Optional"
},
"optionalBlockExplorerUrl": {
"message": "Block Explorer URL (optional)"
},
@ -1438,6 +1472,30 @@
"recipientAddressPlaceholder": {
"message": "Search, public address (0x), or ENS"
},
"recoveryPhraseReminderBackupStart": {
"message": "Start here"
},
"recoveryPhraseReminderConfirm": {
"message": "Got it"
},
"recoveryPhraseReminderHasBackedUp": {
"message": "Always keep your Secret Recovery Phrase in a secure and secret place"
},
"recoveryPhraseReminderHasNotBackedUp": {
"message": "Need to backup your Secret Recovery Phrase again?"
},
"recoveryPhraseReminderItemOne": {
"message": "Never share your Secret Recovery Phrase with anyone"
},
"recoveryPhraseReminderItemTwo": {
"message": "The MetaMask team will never ask for your Secret Recovery Phrase"
},
"recoveryPhraseReminderSubText": {
"message": "Your Secret Recovery Phrase controls all of your accounts."
},
"recoveryPhraseReminderTitle": {
"message": "Protect your funds"
},
"reject": {
"message": "Reject"
},
@ -1949,18 +2007,18 @@
"message": "You are about to swap $1 $2 (~$3) for $4 $5 (~$6).",
"description": "This message represents the price slippage for the swap. $1 and $4 are a number (ex: 2.89), $2 and $5 are symbols (ex: ETH), and $3 and $6 are fiat currency amounts."
},
"swapPriceDifferenceAcknowledgement": {
"message": "I'm aware"
},
"swapPriceDifferenceTitle": {
"message": "Price difference of ~$1%",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceDifferenceTooltip": {
"message": "The difference in market prices can be affected by fees taken by intermediaries, size of market, size of trade, or market inefficiencies."
"swapPriceImpactTooltip": {
"message": "Price impact is the difference between the current market price and the amount received during transaction execution. Price impact is a function of the size of your trade relative to the size of the liquidity pool."
},
"swapPriceDifferenceUnavailable": {
"message": "Market price is unavailable. Make sure you feel comfortable with the returned amount before proceeding."
"swapPriceUnavailableDescription": {
"message": "Price impact could not be determined due to lack of market price data. Please confirm that you are comfortable with the amount of tokens you are about to receive before swapping."
},
"swapPriceUnavailableTitle": {
"message": "Check your rate before proceeding"
},
"swapProcessing": {
"message": "Processing"
@ -2063,13 +2121,13 @@
"message": "Swap $1 to $2",
"description": "Used in the transaction display list to describe a swap. $1 and $2 are the symbols of tokens in involved in a swap."
},
"swapTokenVerificationAddedManually": {
"message": "This token has been added manually."
},
"swapTokenVerificationMessage": {
"message": "Always confirm the token address on $1.",
"description": "Points the user to Etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"Etherscan\" followed by an info icon that shows more info on hover."
},
"swapTokenVerificationNoSource": {
"message": "This token has not been verified."
},
"swapTokenVerificationOnlyOneSource": {
"message": "Only verified on 1 source."
},
@ -2093,9 +2151,6 @@
"message": "Multiple tokens can use the same name and symbol. Check $1 to verify this is the token you're looking for.",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "View $1"
},
"swapYourTokenBalance": {
"message": "$1 $2 available to swap",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"
@ -2149,6 +2204,9 @@
"symbolBetweenZeroTwelve": {
"message": "Symbol must be 11 characters or fewer."
},
"syncInProgress": {
"message": "Sync in progress"
},
"syncWithMobile": {
"message": "Sync with mobile"
},
@ -2219,6 +2277,9 @@
"tokenSymbol": {
"message": "Token Symbol"
},
"tooltipApproveButton": {
"message": "I understand"
},
"total": {
"message": "Total"
},
@ -2333,7 +2394,7 @@
"message": "URLs require the appropriate HTTP/HTTPS prefix."
},
"urlExistsErrorMsg": {
"message": "URL is already present in existing list of networks"
"message": "This URL is currently used by the $1 network."
},
"usePhishingDetection": {
"message": "Use Phishing Detection"
@ -2355,6 +2416,10 @@
"message": "Verify this token on $1",
"description": "Points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\""
},
"verifyThisUnconfirmedTokenOn": {
"message": "Verify this token on $1 and make sure this is the token you want to trade.",
"description": "Points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\""
},
"viewAccount": {
"message": "View Account"
},

View File

@ -249,12 +249,9 @@
"browserNotSupported": {
"message": "El explorador no es compatible…"
},
"builContactList": {
"buildContactList": {
"message": "Cree su lista de contactos"
},
"builtInCalifornia": {
"message": "MetaMask se diseñó y compiló en California."
},
"buy": {
"message": "Comprar"
},
@ -1034,6 +1031,9 @@
"mainnet": {
"message": "Red principal de Ethereum"
},
"makeAnotherSwap": {
"message": "Crear un nuevo canje"
},
"max": {
"message": "Máx."
},
@ -1893,12 +1893,6 @@
"message": "Diferencia de precio de ~$1 %",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceDifferenceTooltip": {
"message": "La diferencia en los precios de mercado puede verse afectada por las tarifas cobradas por los intermediarios, el tamaño del mercado, el tamaño del comercio o las ineficiencias del mercado."
},
"swapPriceDifferenceUnavailable": {
"message": "El precio de mercado no está disponible. Asegúrese de sentirse cómodo con el monto devuelto antes de continuar."
},
"swapProcessing": {
"message": "Procesamiento"
},
@ -2027,9 +2021,6 @@
"message": "Varios tokens pueden usar el mismo nombre y símbolo. Revise $1 para comprobar que este es el token que busca.",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "Ver $1"
},
"swapYourTokenBalance": {
"message": "$1 $2 disponible para canje",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"
@ -2153,6 +2144,9 @@
"tokenSymbol": {
"message": "Símbolo del token"
},
"tooltipApproveButton": {
"message": "Comprendo"
},
"total": {
"message": "Total"
},

View File

@ -249,12 +249,9 @@
"browserNotSupported": {
"message": "El explorador no es compatible…"
},
"builContactList": {
"buildContactList": {
"message": "Cree su lista de contactos"
},
"builtInCalifornia": {
"message": "MetaMask se diseñó y compiló en California."
},
"buy": {
"message": "Comprar"
},
@ -1042,6 +1039,9 @@
"mainnet": {
"message": "Red principal de Ethereum"
},
"makeAnotherSwap": {
"message": "Crear un nuevo canje"
},
"max": {
"message": "Máx."
},
@ -1937,12 +1937,6 @@
"message": "Diferencia de precio de ~$1 %",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceDifferenceTooltip": {
"message": "La diferencia en los precios de mercado puede verse afectada por las tarifas cobradas por los intermediarios, el tamaño del mercado, el tamaño del comercio o las ineficiencias del mercado."
},
"swapPriceDifferenceUnavailable": {
"message": "El precio de mercado no está disponible. Asegúrese de sentirse cómodo con el monto devuelto antes de continuar."
},
"swapProcessing": {
"message": "Procesamiento"
},
@ -2071,9 +2065,6 @@
"message": "Varios tokens pueden usar el mismo nombre y símbolo. Revise $1 para comprobar que este es el token que busca.",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "Ver $1"
},
"swapYourTokenBalance": {
"message": "$1 $2 disponible para canje",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"
@ -2197,6 +2188,9 @@
"tokenSymbol": {
"message": "Símbolo del token"
},
"tooltipApproveButton": {
"message": "Comprendo"
},
"total": {
"message": "Total"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Teie lehitsejat ei toetata..."
},
"builtInCalifornia": {
"message": "MetaMask on projekteeritud ja loodud Californias."
},
"buyWithWyre": {
"message": "Ostke ETH-d Wyre'iga"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "مرورگر شما پشتیبانی نمیشود"
},
"builtInCalifornia": {
"message": "MetaMask در کالیفورنیا طراحی و ساخته شده است."
},
"buyWithWyre": {
"message": "ETH را توسط Wyre خریداری نمایید"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Selaintasi ei tueta..."
},
"builtInCalifornia": {
"message": "MetaMask on suunniteltu ja koottu Kaliforniassa."
},
"buyWithWyre": {
"message": "Osta ETH:ta Wyrella"
},

View File

@ -128,9 +128,6 @@
"browserNotSupported": {
"message": "Hindi sinusuportahan ang iyong Browser..."
},
"builtInCalifornia": {
"message": "Ang MetaMask ay dinisenyo at binuo sa California."
},
"buyWithWyre": {
"message": "Bumili ng ETH gamit ang Wyre"
},

View File

@ -137,9 +137,6 @@
"browserNotSupported": {
"message": "Votre navigateur internet n'est pas supporté..."
},
"builtInCalifornia": {
"message": "MetaMask est designé et developpé en Californie."
},
"buyWithWyre": {
"message": "Acheter ETH avec Wyre"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "הדפדפן שלך אינו נתמך..."
},
"builtInCalifornia": {
"message": "MetaMask תוכנן ונבנה בקליפורניה."
},
"buyWithWyre": {
"message": "רכישת את'ר עם Wyre"
},

View File

@ -249,12 +249,9 @@
"browserNotSupported": {
"message": "आपका ब्राउज़र समर्थित नहीं है..."
},
"builContactList": {
"buildContactList": {
"message": "अपनी संपर्क सूची बनाएं"
},
"builtInCalifornia": {
"message": "MetaMask को कैलिफोर्निया में डिज़ाइन और निर्मित किया गया है।"
},
"buy": {
"message": "खरीदें"
},
@ -1034,6 +1031,9 @@
"mainnet": {
"message": "Ethereum Mainnet"
},
"makeAnotherSwap": {
"message": "एक नया स्वैप बनाएँ"
},
"max": {
"message": "अधिकतम"
},
@ -1893,6 +1893,15 @@
"message": "~$1% का मूल्य अंतर",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceImpactTooltip": {
"message": "मूल्य प्रभाव, वर्तमान बाजार मूल्य और लेन-देन निष्पादन के दौरान प्राप्त राशि के बीच का अंतर है। मूल्य प्रभाव चलनिधि पूल के आकार के सापेक्ष आपके व्यापार के आकार का एक कार्य है।"
},
"swapPriceUnavailableDescription": {
"message": "बाजार मूल्य डेटा की कमी के कारण मूल्य प्रभाव को निर्धारित नहीं किया जा सका। कृपया पुष्टि करें कि आप स्वैप करने से पहले प्राप्त होने वाले टोकन की राशि को लेकर सहज हैं।"
},
"swapPriceUnavailableTitle": {
"message": "आगे बढ़ने से पहले अपने दर की जाँच करें"
},
"swapProcessing": {
"message": "प्रसंस्करण"
},
@ -2021,9 +2030,6 @@
"message": "एकाधिक टोकन एक ही नाम और प्रतीक का उपयोग कर सकते हैं। यह सत्यापित करने के लिए $1 की जाँच करें कि यह वही टोकन है, जिसकी आप तलाश कर रहे हैं।",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "$1 देखें"
},
"swapYourTokenBalance": {
"message": "$1 $2 स्वैप के लिए उपलब्ध है",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"
@ -2147,6 +2153,9 @@
"tokenSymbol": {
"message": "टोकन का प्रतीक"
},
"tooltipApproveButton": {
"message": "मैं समझता हूं"
},
"total": {
"message": "कुलयोग"
},

View File

@ -43,9 +43,6 @@
"blockiesIdenticon": {
"message": "ब्लॉकीज पहचान का उपयोग करें"
},
"builtInCalifornia": {
"message": "मेटामास्क कैलिफ़ोर्निया में डिज़ाइन और बनाया गया है।"
},
"cancel": {
"message": "रद्द करें"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Vaš se preglednik ne podržava..."
},
"builtInCalifornia": {
"message": "MetaMask je osmišljen i izrađen u Kaliforniji."
},
"buyWithWyre": {
"message": "Kupi ETH Wyerom"
},

View File

@ -73,9 +73,6 @@
"browserNotSupported": {
"message": "Navigatè ou a pa sipòte..."
},
"builtInCalifornia": {
"message": "MetaMask fèt e bati nan California."
},
"cancel": {
"message": "Anile"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Az ön böngészője nem támogatott..."
},
"builtInCalifornia": {
"message": "A MetaMaskot Kaliforniában tervezték és hozták létre."
},
"buyWithWyre": {
"message": "Vásároljon ETH-t a Wyre-rel"
},

View File

@ -249,12 +249,9 @@
"browserNotSupported": {
"message": "Browser Anda tidak didukung..."
},
"builContactList": {
"buildContactList": {
"message": "Buat daftar kontak Anda"
},
"builtInCalifornia": {
"message": "MetaMask didesain dan didirikan di California."
},
"buy": {
"message": "Beli"
},
@ -1034,6 +1031,9 @@
"mainnet": {
"message": "Ethereum Mainnet"
},
"makeAnotherSwap": {
"message": "Buat penukaran baru"
},
"max": {
"message": "Maks."
},
@ -1893,6 +1893,15 @@
"message": "Perbedaan harga ~$1%",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceImpactTooltip": {
"message": "Dampak harga adalah selisih antara harga pasar saat ini dan jumlah yang diterima selama terjadinya transaksi. Dampak harga adalah fungsi ukuran dagang relatif terhadap ukuran pool likuiditas."
},
"swapPriceUnavailableDescription": {
"message": "Dampak harga tidak dapat ditentukan karena kurangnya data harga pasar. Harap konfirmasi bahwa Anda setuju dengan jumlah token yang akan Anda terima sebelum penukaran."
},
"swapPriceUnavailableTitle": {
"message": "Periksa tarif Anda sebelum melanjutkan"
},
"swapProcessing": {
"message": "Memproses"
},
@ -2021,9 +2030,6 @@
"message": "Beberapa token dapat menggunakan simbol dan nama yang sama. Periksa $1 untuk memverifikasi inilah token yang Anda cari.",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "Lihat $1"
},
"swapYourTokenBalance": {
"message": "$1 $2 tersedia untuk ditukar",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"
@ -2147,6 +2153,9 @@
"tokenSymbol": {
"message": "Simbol Token"
},
"tooltipApproveButton": {
"message": "Saya paham"
},
"total": {
"message": "Total"
},

View File

@ -217,9 +217,6 @@
"browserNotSupported": {
"message": "Il tuo Browser non è supportato..."
},
"builtInCalifornia": {
"message": "MetaMask è progettato e realizzato in California."
},
"buy": {
"message": "Compra"
},
@ -1621,19 +1618,10 @@
"message": "Stai per scambiare $1 $2 (~$3) per $4 $5 (~$6).",
"description": "This message represents the price slippage for the swap. $1 and $4 are a number (ex: 2.89), $2 and $5 are symbols (ex: ETH), and $3 and $6 are fiat currency amounts."
},
"swapPriceDifferenceAcknowledgement": {
"message": "Sono consapevole"
},
"swapPriceDifferenceTitle": {
"message": "Differenza di prezzo di circa ~$1%",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceDifferenceTooltip": {
"message": "La differenza tra i prezzi del mercato può essere influenzata da commissioni prelevate da intermediari, dimensione del mercato, dimensione dello scambio, o inefficienze del mercato."
},
"swapPriceDifferenceUnavailable": {
"message": "Il prezzo di mercato non è disponibile. Assicurati di sentirti a tuo agio con l'importo restituito prima di procedere."
},
"swapProcessing": {
"message": "In elaborazione"
},
@ -1749,9 +1737,6 @@
"message": "Più token possono usare lo stesso nome e simbolo. Verifica su $1 che questo sia il token che stai cercando.",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "Vedi $1"
},
"swapYourTokenBalance": {
"message": "$1 $2 disponibili allo scambio",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"

View File

@ -249,12 +249,9 @@
"browserNotSupported": {
"message": "ご使用のブラウザーはサポートされていません..."
},
"builContactList": {
"buildContactList": {
"message": "連絡先リストを作成する"
},
"builtInCalifornia": {
"message": "MetaMask はカリフォルニアで設計および作成されました。"
},
"buy": {
"message": "購入"
},
@ -1034,6 +1031,9 @@
"mainnet": {
"message": "イーサリアム メインネット"
},
"makeAnotherSwap": {
"message": "新しいスワップの作成"
},
"max": {
"message": "最大"
},
@ -1893,12 +1893,6 @@
"message": "約 $1% の価格差",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceDifferenceTooltip": {
"message": "市場価格の違いは、仲介業者が負担する手数料、市場規模、取引量、または取引価格差の影響を受けることがあります。"
},
"swapPriceDifferenceUnavailable": {
"message": "マーケット価格は利用できません。続行する前に、返金額に問題がないことを確認してください。"
},
"swapProcessing": {
"message": "処理中"
},
@ -2027,9 +2021,6 @@
"message": "複数のトークンが同じ名前とシンボルを使用できます。$1 をチェックして、これが探しているトークンであることを確認します。",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "$1 を表示"
},
"swapYourTokenBalance": {
"message": "$1 $2 はスワップに使用可能です",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"
@ -2153,6 +2144,9 @@
"tokenSymbol": {
"message": "トークン シンボル"
},
"tooltipApproveButton": {
"message": "理解しました"
},
"total": {
"message": "合計"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "ನಿಮ್ಮ ಬ್ರೌಸರ್ ಬೆಂಬಲಿಸುತ್ತಿಲ್ಲ..."
},
"builtInCalifornia": {
"message": "MetaMask ಅನ್ನು ವಿನ್ಯಾಸಗೊಳಿಸಲಾಗಿದೆ ಮತ್ತು ಕ್ಯಾಲಿಫೋರ್ನಿಯಾದಲ್ಲಿ ನಿರ್ಮಿಸಲಾಗಿದೆ."
},
"buyWithWyre": {
"message": "Wyre ನೊಂದಿಗೆ ETH ಖರೀದಿಸಿ"
},

View File

@ -249,12 +249,9 @@
"browserNotSupported": {
"message": "지원되지 않는 브라우저입니다..."
},
"builContactList": {
"buildContactList": {
"message": "연락처 목록 작성하기"
},
"builtInCalifornia": {
"message": "MetaMask는 캘리포니아에서 설계 및 제작됩니다."
},
"buy": {
"message": "구매"
},
@ -1038,6 +1035,9 @@
"mainnet": {
"message": "이더리움 메인넷"
},
"makeAnotherSwap": {
"message": "새 스왑 생성"
},
"max": {
"message": "최대"
},
@ -1933,6 +1933,15 @@
"message": "~$1%의 가격 차이",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceImpactTooltip": {
"message": "가격 영향은 현재 시장 가격과 거래 실행 도중 받은 금액 사이의 차이입니다. 가격 영향은 유동성 풀의 크기 대비 거래의 크기를 나타내는 함수입니다."
},
"swapPriceUnavailableDescription": {
"message": "시장 가격 데이터가 부족하여 가격 영향을 파악할 수 없습니다. 스왑하기 전에 받게 될 토큰 수에 만족하시는지 확인하시기 바랍니다."
},
"swapPriceUnavailableTitle": {
"message": "진행하기 전에 요율을 확인하십시오."
},
"swapProcessing": {
"message": "처리 중"
},
@ -2061,9 +2070,6 @@
"message": "여러 토큰이 같은 이름과 기호를 사용할 수 있습니다. $1을(를) 확인하여 원하는 토큰인지 확인하세요.",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "$1 보기"
},
"swapYourTokenBalance": {
"message": "$1 $2 스왑 가능",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"
@ -2187,6 +2193,9 @@
"tokenSymbol": {
"message": "토큰 기호"
},
"tooltipApproveButton": {
"message": "이해했습니다."
},
"total": {
"message": "합계"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Jūsų naršyklė neatpažįstama..."
},
"builtInCalifornia": {
"message": "„MetaMask“ suprojektuota ir įdiegta Kalifornijoje."
},
"buyWithWyre": {
"message": "Pirkti ETH su „Wyre“"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Jūsu pārlūkprogramma netiek atbalstīta..."
},
"builtInCalifornia": {
"message": "MetaMask ir izstrādāta un izveidota Kalifornijā."
},
"buyWithWyre": {
"message": "Pirkt ETH ar Wyre"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Pelayar anda tidak disokong..."
},
"builtInCalifornia": {
"message": "MetaMask direka dan dibina di California."
},
"buyWithWyre": {
"message": "Beli ETH dengan Wyre"
},

View File

@ -40,9 +40,6 @@
"blockiesIdenticon": {
"message": "Gebruik Blockies Identicon"
},
"builtInCalifornia": {
"message": "MetaMask is ontworpen en gebouwd in Californië."
},
"cancel": {
"message": "Annuleer"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Nettleseren din støttes ikke ..."
},
"builtInCalifornia": {
"message": "MetaMask ble bygget og designet i California."
},
"buyWithWyre": {
"message": "Kjøp ETH med Wyre"
},

View File

@ -249,12 +249,9 @@
"browserNotSupported": {
"message": "Hindi sinusuportahan ang iyong Browser..."
},
"builContactList": {
"buildContactList": {
"message": "Buuin ang iyong listahan ng contact"
},
"builtInCalifornia": {
"message": "Ang MetaMask ay idinisenyo at binuo sa California."
},
"buy": {
"message": "Bumili"
},
@ -1042,6 +1039,9 @@
"mainnet": {
"message": "Ethereum Mainnet"
},
"makeAnotherSwap": {
"message": "Gumawa ng bagong swap"
},
"max": {
"message": "Max"
},
@ -1937,6 +1937,15 @@
"message": "Kaibahan sa presyo na ~$1%",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceImpactTooltip": {
"message": "Ang epekto sa presyo ay ang pagkakaiba sa kasalukuyang presyo sa merkado at sa halagang natanggap sa pag-execute ng transaksyon. Ang epekto sa presyo ay isang function ng laki ng iyong trade kumpara sa laki ng liquidity pool."
},
"swapPriceUnavailableDescription": {
"message": "Hindi natukoy ang epekto sa presyo dahil sa kakulangan ng data sa presyo sa merkado. Pakikumpirma na kumportable ka sa dami ng mga token na matatanggap mo bago makipag-swap."
},
"swapPriceUnavailableTitle": {
"message": "Tingnan ang iyong rate bago magpatuloy"
},
"swapProcessing": {
"message": "Pagproseso"
},
@ -2188,6 +2197,9 @@
"tokenSymbol": {
"message": "Simbolo ng Token"
},
"tooltipApproveButton": {
"message": "Nauunawaan ko"
},
"total": {
"message": "Kabuuan"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Twoja przeglądarka nie jest obsługiwana..."
},
"builtInCalifornia": {
"message": "MetaMask został zaprojektowany i stworzony w Kaliforni."
},
"buyWithWyre": {
"message": "Kup ETH poprzez Wyre"
},

View File

@ -43,9 +43,6 @@
"blockiesIdenticon": {
"message": "Usar Blockies Identicon"
},
"builtInCalifornia": {
"message": "MetaMask é desenhada e construída na California."
},
"cancel": {
"message": "Cancelar"
},

View File

@ -249,12 +249,9 @@
"browserNotSupported": {
"message": "Seu navegador não é compatível..."
},
"builContactList": {
"buildContactList": {
"message": "Crie sua lista de contatos"
},
"builtInCalifornia": {
"message": "O MetaMask é projetado e construído na Califórnia."
},
"buy": {
"message": "Comprar"
},
@ -1028,6 +1025,9 @@
"mainnet": {
"message": "Mainnet do Ethereum"
},
"makeAnotherSwap": {
"message": "Criar novo swap"
},
"max": {
"message": "Máx"
},
@ -1877,6 +1877,15 @@
"message": "Diferença de preço de aproximadamente $1%",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceImpactTooltip": {
"message": "O impacto no preço é a diferença entre o preço de mercado atual e o valor recebido durante a execução da transação. O impacto no preço é uma função do tamanho do seu comércio em relação ao tamanho do pool de liquidez."
},
"swapPriceUnavailableDescription": {
"message": "O impacto no preço não poderia ser determinado devido aos dados do preço de mercado. Confirme que você está satisfeito com o valor dos tokens que você está prestes a receber antes de fazer swap."
},
"swapPriceUnavailableTitle": {
"message": "Verifique sua taxa antes de continuar"
},
"swapProcessing": {
"message": "Processando"
},
@ -2128,6 +2137,9 @@
"tokenSymbol": {
"message": "Símbolo do token"
},
"tooltipApproveButton": {
"message": "Eu entendo"
},
"total": {
"message": "Total"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Browserul dvs. nu este compatibil..."
},
"builtInCalifornia": {
"message": "MetaMask este concepută și creată în California."
},
"buyWithWyre": {
"message": "Cumpărați ETH cu Wyre"
},

View File

@ -249,12 +249,9 @@
"browserNotSupported": {
"message": "Ваш браузер не поддерживается..."
},
"builContactList": {
"buildContactList": {
"message": "Создайте список контактов"
},
"builtInCalifornia": {
"message": "MetaMask разработан и построен в Калифорнии."
},
"buy": {
"message": "Купить"
},
@ -1034,6 +1031,9 @@
"mainnet": {
"message": "Сеть Ethereum Mainnet"
},
"makeAnotherSwap": {
"message": "Создать новый своп"
},
"max": {
"message": "Макс."
},
@ -1893,6 +1893,15 @@
"message": "Разница в цене составляет ~$1%",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceImpactTooltip": {
"message": "Колебание цены — это разница между текущей рыночной ценой и суммой, полученной во время выполнения транзакции. Колебание цены зависит от размера вашей сделки относительно размера пула ликвидности."
},
"swapPriceUnavailableDescription": {
"message": "Колебание цены определить не удалось из-за отсутствия данных о рыночных ценах. Перед свопом подтвердите, что вас устраивает количество токенов, которое вы получите."
},
"swapPriceUnavailableTitle": {
"message": "Прежде чем продолжить, проверьте курс"
},
"swapProcessing": {
"message": "Обработка"
},
@ -2021,9 +2030,6 @@
"message": "Несколько токенов могут использовать одно и то же имя и символ. Убедитесь, что это именно тот токен, который вы ищете, на $1.",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "Просмотреть $1"
},
"swapYourTokenBalance": {
"message": "$1 $2 доступны для свопа",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"
@ -2147,6 +2153,9 @@
"tokenSymbol": {
"message": "Символ токена"
},
"tooltipApproveButton": {
"message": "Я понимаю"
},
"total": {
"message": "Итого"
},

View File

@ -137,9 +137,6 @@
"browserNotSupported": {
"message": "Váš prehliadač nie je podporovaný..."
},
"builtInCalifornia": {
"message": "MetaMask je navržen a vytvořen v Kalifornii."
},
"buyWithWyre": {
"message": "Kúpte ETH s Wyre"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Vaš brskalnik ni podptrt ..."
},
"builtInCalifornia": {
"message": "MetaMask je zasnovan in ustvarjen v Kaliforniji."
},
"buyWithWyre": {
"message": "Kupi ETH z Wyre"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Vaš pregledač nije podržan..."
},
"builtInCalifornia": {
"message": "MetaMask je dizajniran i izgrađen u Kaliforniji."
},
"buyWithWyre": {
"message": "Kupite ETH preko servisa Wyre"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Din webbläsare stöds inte..."
},
"builtInCalifornia": {
"message": "MetaMask är skapat och utformat i Kalifornien."
},
"buyWithWyre": {
"message": "Köp ETH med Wyre"
},

View File

@ -140,9 +140,6 @@
"browserNotSupported": {
"message": "Kivinjari chaku hakiwezeshwi..."
},
"builtInCalifornia": {
"message": "MetaMask imeundwa na kutengenezwa California."
},
"buyWithWyre": {
"message": "Nunua ETH kwa kutumia Wyre"
},

View File

@ -55,9 +55,6 @@
"blockiesIdenticon": {
"message": "ப்ளாக்கிஸ் ஐடென்டிகோன் பயன்பாட்டு"
},
"builtInCalifornia": {
"message": "மேடமஸ்க் வடிவமைக்கப்பட்டு கலிபோர்னியாவில் கட்டப்பட்டுள்ளது."
},
"cancel": {
"message": "ரத்து செய்"
},

View File

@ -49,9 +49,6 @@
"blockiesIdenticon": {
"message": "ใช้งาน Blockies Identicon"
},
"builtInCalifornia": {
"message": "MetaMask ออกแบบและพัฒนาที่แคลิฟอร์เนีย"
},
"cancel": {
"message": "ยกเลิก"
},

View File

@ -211,9 +211,6 @@
"browserNotSupported": {
"message": "Hindi sinusuportahan ang iyong Browser..."
},
"builtInCalifornia": {
"message": "Ang MetaMask ay idinisenyo at binuo sa California."
},
"buy": {
"message": "Bilhin"
},
@ -1699,9 +1696,6 @@
"message": "Maaaring gamitin ng maraming token ang iisang pangalan at simbolo. Suriin ang $1 para ma-verify na ito ang token na hinahanap mo.",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "Tingnan ang $1"
},
"swapYourTokenBalance": {
"message": "Available ang $1 $2 na i-swap",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"

View File

@ -46,9 +46,6 @@
"blockiesIdenticon": {
"message": "Blockies Identicon kullan"
},
"builtInCalifornia": {
"message": "MetaMask California'da tasarlandı ve yaratıldı"
},
"cancel": {
"message": "Vazgeç"
},

View File

@ -143,9 +143,6 @@
"browserNotSupported": {
"message": "Ваш браузер не підтримується..."
},
"builtInCalifornia": {
"message": "MetaMask розроблено й створено в Каліфорнії."
},
"buyWithWyre": {
"message": "Купити ETH через Wyre"
},

View File

@ -249,12 +249,9 @@
"browserNotSupported": {
"message": "Trình duyệt của bạn không được hỗ trợ..."
},
"builContactList": {
"buildContactList": {
"message": "Xây dựng danh sách liên hệ của bạn"
},
"builtInCalifornia": {
"message": "MetaMask được thiết kế và phát triển tại California."
},
"buy": {
"message": "Mua"
},
@ -1034,6 +1031,9 @@
"mainnet": {
"message": "Mạng chính thức của Ethereum"
},
"makeAnotherSwap": {
"message": "Tạo một giao dịch hoán đổi mới"
},
"max": {
"message": "Tối đa"
},
@ -1893,6 +1893,15 @@
"message": "Chênh lệch giá ~$1%",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceImpactTooltip": {
"message": "Tác động về giá là mức chênh lệch giữa giá thị trường hiện tại và số tiền nhận được trong quá trình thực hiện giao dịch. Tác động giá là một hàm trong quy mô giao dịch của bạn so với quy mô của nhóm thanh khoản."
},
"swapPriceUnavailableDescription": {
"message": "Không thể xác định tác động giá do thiếu dữ liệu giá thị trường. Vui lòng xác nhận rằng bạn cảm thấy thoải mái với số lượng token bạn sắp nhận được trước khi hoán đổi."
},
"swapPriceUnavailableTitle": {
"message": "Hãy kiểm tra tỷ giá trước khi tiếp tục"
},
"swapProcessing": {
"message": "Đang xử lý"
},
@ -2021,9 +2030,6 @@
"message": "Nhiều token có thể dùng cùng một tên và ký hiệu. Hãy kiểm tra $1 để xác minh xem đây có phải là token bạn đang tìm kiếm không.",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "Xem $1"
},
"swapYourTokenBalance": {
"message": "Có sẵn $1 $2 để hoán đổi",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"
@ -2147,6 +2153,9 @@
"tokenSymbol": {
"message": "Ký hiệu token"
},
"tooltipApproveButton": {
"message": "Tôi đã hiểu"
},
"total": {
"message": "Tổng"
},

View File

@ -217,9 +217,6 @@
"browserNotSupported": {
"message": "您的浏览器不支持该功能……"
},
"builtInCalifornia": {
"message": "MetaMask在加利福尼亚设计和制造。"
},
"buy": {
"message": "购买"
},
@ -1619,12 +1616,6 @@
"message": "价格差异 ~$1%",
"description": "$1 is a number (ex: 1.23) that represents the price difference."
},
"swapPriceDifferenceTooltip": {
"message": "市场价格的差异可能受到中介机构收取的费用、市场规模、交易规模或市场效率低下的影响。"
},
"swapPriceDifferenceUnavailable": {
"message": "市场价格不可用。 请确认您对退回的数额感到满意后再继续。"
},
"swapProcessing": {
"message": "处理中"
},
@ -1726,9 +1717,6 @@
"message": "多个代币可以使用相同的名称和符号。检查 $1以太坊浏览器以确认这是您正在寻找的代币。",
"description": "This appears in a tooltip next to the verifyThisTokenOn message. It gives the user more information about why they should check the token on a block explorer. $1 will be the name or url of the block explorer, which will be the translation of 'etherscan' or a block explorer url specified for a custom network."
},
"swapViewToken": {
"message": "查看 $1"
},
"swapYourTokenBalance": {
"message": "$1 $2 可用",
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"

View File

@ -149,9 +149,6 @@
"browserNotSupported": {
"message": "您的瀏覽器尚未支援..."
},
"builtInCalifornia": {
"message": "MetaMask 是在加州設計製造"
},
"buy": {
"message": "買"
},

View File

@ -19,6 +19,7 @@ import {
ENVIRONMENT_TYPE_NOTIFICATION,
ENVIRONMENT_TYPE_FULLSCREEN,
} from '../../shared/constants/app';
import { SECOND } from '../../shared/constants/time';
import migrations from './migrations';
import Migrator from './lib/migrator';
import ExtensionPlatform from './platforms/extension';
@ -491,7 +492,7 @@ async function openPopup() {
clearInterval(interval);
resolve();
}
}, 1000);
}, SECOND);
});
}

View File

@ -1,6 +1,7 @@
import EventEmitter from 'events';
import { ObservableStore } from '@metamask/obs-store';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import { MINUTE } from '../../../shared/constants/time';
export default class AppStateController extends EventEmitter {
/**
@ -24,6 +25,8 @@ export default class AppStateController extends EventEmitter {
connectedStatusPopoverHasBeenShown: true,
defaultHomeActiveTabName: null,
browserEnvironment: {},
recoveryPhraseReminderHasBeenShown: false,
recoveryPhraseReminderLastShown: new Date().getTime(),
...initState,
});
this.timer = null;
@ -112,6 +115,27 @@ export default class AppStateController extends EventEmitter {
});
}
/**
* Record that the user has been shown the recovery phrase reminder
* @returns {void}
*/
setRecoveryPhraseReminderHasBeenShown() {
this.store.updateState({
recoveryPhraseReminderHasBeenShown: true,
});
}
/**
* Record the timestamp of the last time the user has seen the recovery phrase reminder
* @param {number} lastShown - timestamp when user was last shown the reminder
* @returns {void}
*/
setRecoveryPhraseReminderLastShown(lastShown) {
this.store.updateState({
recoveryPhraseReminderLastShown: lastShown,
});
}
/**
* Sets the last active time to the current time
* @returns {void}
@ -156,7 +180,7 @@ export default class AppStateController extends EventEmitter {
this.timer = setTimeout(
() => this.onInactiveTimeout(),
timeoutMinutes * 60 * 1000,
timeoutMinutes * MINUTE,
);
}

View File

@ -4,9 +4,10 @@ import { warn } from 'loglevel';
import SINGLE_CALL_BALANCES_ABI from 'single-call-balance-checker-abi';
import { MAINNET_CHAIN_ID } from '../../../shared/constants/network';
import { SINGLE_CALL_BALANCES_ADDRESS } from '../constants/contracts';
import { MINUTE } from '../../../shared/constants/time';
// By default, poll every 3 minutes
const DEFAULT_INTERVAL = 180 * 1000;
const DEFAULT_INTERVAL = MINUTE * 3;
/**
* A controller that polls for token exchange

View File

@ -18,8 +18,9 @@ import {
RINKEBY_CHAIN_ID,
ROPSTEN_CHAIN_ID,
} from '../../../shared/constants/network';
import { SECOND } from '../../../shared/constants/time';
const fetchWithTimeout = getFetchWithTimeout(30000);
const fetchWithTimeout = getFetchWithTimeout(SECOND * 30);
/**
* @typedef {import('../../../shared/constants/transaction').TransactionMeta} TransactionMeta

View File

@ -19,6 +19,7 @@ import {
TRANSACTION_TYPES,
TRANSACTION_STATUSES,
} from '../../../shared/constants/transaction';
import { MILLISECOND } from '../../../shared/constants/time';
const IncomingTransactionsController = proxyquire('./incoming-transactions', {
'../../../shared/modules/random-id': { default: () => 54321 },
@ -26,7 +27,7 @@ const IncomingTransactionsController = proxyquire('./incoming-transactions', {
const FAKE_CHAIN_ID = '0x1338';
const MOCK_SELECTED_ADDRESS = '0x0101';
const SET_STATE_TIMEOUT = 10;
const SET_STATE_TIMEOUT = MILLISECOND * 10;
const EXISTING_INCOMING_TX = { id: 777, hash: '0x123456' };
const PREPOPULATED_INCOMING_TXS_BY_HASH = {

View File

@ -6,9 +6,10 @@ import createInflightMiddleware from 'eth-json-rpc-middleware/inflight-cache';
import createBlockTrackerInspectorMiddleware from 'eth-json-rpc-middleware/block-tracker-inspector';
import providerFromMiddleware from 'eth-json-rpc-middleware/providerFromMiddleware';
import { PollingBlockTracker } from 'eth-block-tracker';
import { SECOND } from '../../../../shared/constants/time';
const inTest = process.env.IN_TEST === 'true';
const blockTrackerOpts = inTest ? { pollingInterval: 1000 } : {};
const blockTrackerOpts = inTest ? { pollingInterval: SECOND } : {};
const getTestMiddlewares = () => {
return inTest ? [createEstimateGasDelayTestMiddleware()] : [];
};
@ -51,7 +52,7 @@ function createChainIdMiddleware(chainId) {
function createEstimateGasDelayTestMiddleware() {
return createAsyncMiddleware(async (req, _, next) => {
if (req.method === 'eth_estimateGas') {
await new Promise((resolve) => setTimeout(resolve, 2000));
await new Promise((resolve) => setTimeout(resolve, SECOND * 2));
}
return next();
});

View File

@ -19,6 +19,7 @@ import {
RINKEBY_CHAIN_ID,
INFURA_BLOCKED_KEY,
} from '../../../../shared/constants/network';
import { SECOND } from '../../../../shared/constants/time';
import {
isPrefixedFormattedHexString,
isSafeChainId,
@ -29,7 +30,7 @@ import createInfuraClient from './createInfuraClient';
import createJsonRpcClient from './createJsonRpcClient';
const env = process.env.METAMASK_ENV;
const fetchWithTimeout = getFetchWithTimeout(30000);
const fetchWithTimeout = getFetchWithTimeout(SECOND * 30);
let defaultProviderConfigOpts;
if (process.env.IN_TEST === 'true') {
@ -205,7 +206,7 @@ export default class NetworkController extends EventEmitter {
});
}
async setProviderType(type, rpcUrl = '', ticker = 'ETH', nickname = '') {
async setProviderType(type) {
assert.notStrictEqual(
type,
NETWORK_TYPE_RPC,
@ -216,7 +217,13 @@ export default class NetworkController extends EventEmitter {
`Unknown Infura provider type "${type}".`,
);
const { chainId } = NETWORK_TYPE_TO_ID_MAP[type];
this.setProviderConfig({ type, rpcUrl, chainId, ticker, nickname });
this.setProviderConfig({
type,
rpcUrl: '',
chainId,
ticker: 'ETH',
nickname: '',
});
}
resetConnection() {

View File

@ -1,4 +1,5 @@
import { strict as assert } from 'assert';
import { GAS_LIMITS } from '../../../../shared/constants/gas';
import { txMetaStub } from '../../../../test/stub/tx-meta-stub';
import {
createPendingNonceMiddleware,
@ -55,7 +56,7 @@ describe('PendingNonceMiddleware', function () {
blockHash: null,
blockNumber: null,
from: '0xf231d46dd78806e1dd93442cf33c7671f8538748',
gas: '0x5208',
gas: GAS_LIMITS.SIMPLE,
gasPrice: '0x1e8480',
hash:
'0x2cc5a25744486f7383edebbf32003e5a66e18135799593d6b5cdd2bb43674f09',

View File

@ -14,6 +14,7 @@ import {
SWAPS_FETCH_ORDER_CONFLICT,
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP,
} from '../../../shared/constants/swaps';
import { isSwapsDefaultTokenAddress } from '../../../shared/modules/swaps.utils';
import {
@ -21,6 +22,7 @@ import {
fetchSwapsFeatureLiveness as defaultFetchSwapsFeatureLiveness,
fetchSwapsQuoteRefreshTime as defaultFetchSwapsQuoteRefreshTime,
} from '../../../ui/pages/swaps/swaps.util';
import { MINUTE, SECOND } from '../../../shared/constants/time';
import { NETWORK_EVENTS } from './network';
// The MAX_GAS_LIMIT is a number that is higher than the maximum gas costs we have observed on any aggregator
@ -32,11 +34,11 @@ const POLL_COUNT_LIMIT = 3;
// If for any reason the MetaSwap API fails to provide a refresh time,
// provide a reasonable fallback to avoid further errors
const FALLBACK_QUOTE_REFRESH_TIME = 60000;
const FALLBACK_QUOTE_REFRESH_TIME = MINUTE;
// This is the amount of time to wait, after successfully fetching quotes
// and their gas estimates, before fetching for new quotes
const QUOTE_POLLING_DIFFERENCE_INTERVAL = 10 * 1000;
const QUOTE_POLLING_DIFFERENCE_INTERVAL = SECOND * 10;
function calculateGasEstimateWithRefund(
maxGas = MAX_GAS_LIMIT,
@ -346,7 +348,7 @@ export default class SwapsController {
const gasTimeout = setTimeout(() => {
gasTimedOut = true;
resolve({ gasLimit: null, simulationFails: true });
}, 5000);
}, SECOND * 5);
// Remove gas from params that will be passed to the `estimateGas` call
// Including it can cause the estimate to fail if the actual gas needed

View File

@ -12,6 +12,7 @@ import {
} from '../../../shared/constants/network';
import { ETH_SWAPS_TOKEN_OBJECT } from '../../../shared/constants/swaps';
import { createTestProviderTools } from '../../../test/stub/provider';
import { SECOND } from '../../../shared/constants/time';
import SwapsController, { utils } from './swaps';
import { NETWORK_EVENTS } from './network';
@ -34,6 +35,8 @@ const TEST_AGG_ID_6 = 'TEST_AGG_6';
const TEST_AGG_ID_BEST = 'TEST_AGG_BEST';
const TEST_AGG_ID_APPROVAL = 'TEST_AGG_APPROVAL';
const POLLING_TIMEOUT = SECOND * 1000;
const MOCK_APPROVAL_NEEDED = {
data:
'0x095ea7b300000000000000000000000095e6f48254609a6ee006f7d493c8e5fb97094cef0000000000000000000000000000000000000000004a817c7ffffffdabf41c00',
@ -836,7 +839,7 @@ describe('SwapsController', function () {
it('clears polling timeout', function () {
swapsController.pollingTimeout = setTimeout(
() => assert.fail(),
1000000,
POLLING_TIMEOUT,
);
swapsController.resetSwapsState();
assert.strictEqual(swapsController.pollingTimeout._idleTimeout, -1);
@ -847,7 +850,7 @@ describe('SwapsController', function () {
it('clears polling timeout', function () {
swapsController.pollingTimeout = setTimeout(
() => assert.fail(),
1000000,
POLLING_TIMEOUT,
);
swapsController.stopPollingForQuotes();
assert.strictEqual(swapsController.pollingTimeout._idleTimeout, -1);
@ -865,7 +868,7 @@ describe('SwapsController', function () {
it('clears polling timeout', function () {
swapsController.pollingTimeout = setTimeout(
() => assert.fail(),
1000000,
POLLING_TIMEOUT,
);
swapsController.resetPostFetchState();
assert.strictEqual(swapsController.pollingTimeout._idleTimeout, -1);

View File

@ -3,11 +3,12 @@ import log from 'loglevel';
import { normalize as normalizeAddress } from 'eth-sig-util';
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout';
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
import { MINUTE, SECOND } from '../../../shared/constants/time';
const fetchWithTimeout = getFetchWithTimeout(30000);
const fetchWithTimeout = getFetchWithTimeout(SECOND * 30);
// By default, poll every 3 minutes
const DEFAULT_INTERVAL = 180 * 1000;
const DEFAULT_INTERVAL = MINUTE * 3;
/**
* A controller that polls for token exchange

View File

@ -23,6 +23,7 @@ import {
TRANSACTION_TYPES,
} from '../../../../shared/constants/transaction';
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
import { GAS_LIMITS } from '../../../../shared/constants/gas';
import TransactionStateManager from './tx-state-manager';
import TxGasUtil from './tx-gas-utils';
import PendingTransactionTracker from './pending-tx-tracker';
@ -30,7 +31,6 @@ import * as txUtils from './lib/util';
const hstInterface = new ethers.utils.Interface(abi);
const SIMPLE_GAS_COST = '0x5208'; // Hex for 21000, cost of a simple send.
const MAX_MEMSTORE_TX_LIST_SIZE = 100; // Number of transactions (by unique nonces) to keep in memory
/**
@ -366,7 +366,7 @@ export default class TransactionController extends EventEmitter {
}
// This is a standard ether simple send, gas requirement is exactly 21k
return { gasLimit: SIMPLE_GAS_COST };
return { gasLimit: GAS_LIMITS.SIMPLE };
}
const {
@ -404,7 +404,7 @@ export default class TransactionController extends EventEmitter {
from,
to: from,
nonce,
gas: customGasLimit || '0x5208',
gas: customGasLimit || GAS_LIMITS.SIMPLE,
value: '0x0',
gasPrice: newGasPrice,
},

View File

@ -13,6 +13,7 @@ import {
TRANSACTION_STATUSES,
TRANSACTION_TYPES,
} from '../../../../shared/constants/transaction';
import { SECOND } from '../../../../shared/constants/time';
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
import TransactionController from '.';
@ -468,7 +469,7 @@ describe('Transaction Controller', function () {
},
};
// eslint-disable-next-line @babel/no-invalid-this
this.timeout(15000);
this.timeout(SECOND * 15);
const wrongValue = '0x05';
txController.addTransaction(txMeta);

View File

@ -1,18 +1,36 @@
import { ObservableStore } from '@metamask/obs-store';
/**
* @typedef {import('@metamask/controllers').ControllerMessenger} ControllerMessenger
*/
/**
* An ObservableStore that can composes a flat
* structure of child stores based on configuration
*/
export default class ComposableObservableStore extends ObservableStore {
/**
* 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.
* @type {Record<string, Object>}
*/
config = {};
/**
* Create a new store
*
* @param {Object} [initState] - The initial store state
* @param {Object} [config] - Map of internal state keys to child stores
* @param {Object} options
* @param {Object} [options.config] - Map of internal state keys to child stores
* @param {ControllerMessenger} options.controllerMessenger - The controller
* messenger, used for subscribing to events from BaseControllerV2-based
* controllers.
* @param {Object} [options.state] - The initial store state
*/
constructor(initState, config) {
super(initState);
constructor({ config, controllerMessenger, state }) {
super(state);
this.controllerMessenger = controllerMessenger;
if (config) {
this.updateStructure(config);
}
@ -21,15 +39,31 @@ export default class ComposableObservableStore extends ObservableStore {
/**
* Composes a new internal store subscription structure
*
* @param {Object} [config] - Map of internal state keys to child stores
* @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.
*/
updateStructure(config) {
this.config = config;
this.removeAllListeners();
for (const key of Object.keys(this.config)) {
config[key].subscribe((state) => {
this.updateState({ [key]: state });
});
for (const key of Object.keys(config)) {
if (!config[key]) {
throw new Error(`Undefined '${key}'`);
}
const store = config[key];
if (store.subscribe) {
config[key].subscribe((state) => {
this.updateState({ [key]: state });
});
} else {
this.controllerMessenger.subscribe(
`${store.name}:stateChange`,
(state) => {
this.updateState({ [key]: state });
},
);
}
}
}

View File

@ -1,40 +1,194 @@
import { strict as assert } from 'assert';
import { ObservableStore } from '@metamask/obs-store';
import {
BaseController,
BaseControllerV2,
ControllerMessenger,
} from '@metamask/controllers';
import ComposableObservableStore from './ComposableObservableStore';
class OldExampleController extends BaseController {
name = 'OldExampleController';
defaultState = {
baz: 'baz',
};
constructor() {
super();
this.initialize();
}
updateBaz(contents) {
this.update({ baz: contents });
}
}
class ExampleController extends BaseControllerV2 {
static defaultState = {
bar: 'bar',
};
static metadata = {
bar: { persist: true, anonymous: true },
};
constructor({ messenger }) {
super({
messenger,
name: 'ExampleController',
metadata: ExampleController.metadata,
state: ExampleController.defaultState,
});
}
updateBar(contents) {
this.update(() => {
return { bar: contents };
});
}
}
describe('ComposableObservableStore', function () {
it('should register initial state', function () {
const store = new ComposableObservableStore('state');
const controllerMessenger = new ControllerMessenger();
const store = new ComposableObservableStore({
controllerMessenger,
state: 'state',
});
assert.strictEqual(store.getState(), 'state');
});
it('should register initial structure', function () {
const controllerMessenger = new ControllerMessenger();
const testStore = new ObservableStore();
const store = new ComposableObservableStore(null, { TestStore: testStore });
const store = new ComposableObservableStore({
config: { TestStore: testStore },
controllerMessenger,
});
testStore.putState('state');
assert.deepEqual(store.getState(), { TestStore: 'state' });
});
it('should update structure', function () {
it('should update structure with observable store', function () {
const controllerMessenger = new ControllerMessenger();
const testStore = new ObservableStore();
const store = new ComposableObservableStore();
const store = new ComposableObservableStore({ controllerMessenger });
store.updateStructure({ TestStore: testStore });
testStore.putState('state');
assert.deepEqual(store.getState(), { TestStore: 'state' });
});
it('should return flattened state', function () {
const fooStore = new ObservableStore({ foo: 'foo' });
const barStore = new ObservableStore({ bar: 'bar' });
const store = new ComposableObservableStore(null, {
FooStore: fooStore,
BarStore: barStore,
it('should update structure with BaseController-based controller', function () {
const controllerMessenger = new ControllerMessenger();
const oldExampleController = new OldExampleController();
const store = new ComposableObservableStore({ controllerMessenger });
store.updateStructure({ OldExample: oldExampleController });
oldExampleController.updateBaz('state');
assert.deepEqual(store.getState(), { OldExample: { baz: 'state' } });
});
it('should update structure with BaseControllerV2-based controller', function () {
const controllerMessenger = new ControllerMessenger();
const exampleController = new ExampleController({
messenger: controllerMessenger,
});
const store = new ComposableObservableStore({ controllerMessenger });
store.updateStructure({ Example: exampleController });
exampleController.updateBar('state');
console.log(exampleController.state);
assert.deepEqual(store.getState(), { Example: { bar: 'state' } });
});
it('should update structure with all three types of stores', function () {
const controllerMessenger = new ControllerMessenger();
const exampleStore = new ObservableStore();
const exampleController = new ExampleController({
messenger: controllerMessenger,
});
const oldExampleController = new OldExampleController();
const store = new ComposableObservableStore({ controllerMessenger });
store.updateStructure({
Example: exampleController,
OldExample: oldExampleController,
Store: exampleStore,
});
exampleStore.putState('state');
exampleController.updateBar('state');
oldExampleController.updateBaz('state');
assert.deepEqual(store.getState(), {
Example: { bar: 'state' },
OldExample: { baz: 'state' },
Store: 'state',
});
});
it('should return flattened state', function () {
const controllerMessenger = new ControllerMessenger();
const fooStore = new ObservableStore({ foo: 'foo' });
const barController = new ExampleController({
messenger: controllerMessenger,
});
const bazController = new OldExampleController();
const store = new ComposableObservableStore({
config: {
FooStore: fooStore,
BarStore: barController,
BazStore: bazController,
},
controllerMessenger,
state: {
FooStore: fooStore.getState(),
BarStore: barController.state,
BazStore: bazController.state,
},
});
assert.deepEqual(store.getFlatState(), {
foo: 'foo',
bar: 'bar',
baz: 'baz',
});
assert.deepEqual(store.getFlatState(), { foo: 'foo', bar: 'bar' });
});
it('should return empty flattened state when not configured', function () {
const store = new ComposableObservableStore();
const controllerMessenger = new ControllerMessenger();
const store = new ComposableObservableStore({ controllerMessenger });
assert.deepEqual(store.getFlatState(), {});
});
it('should throw if the controller messenger is omitted and the config includes a BaseControllerV2 controller', function () {
const controllerMessenger = new ControllerMessenger();
const exampleController = new ExampleController({
messenger: controllerMessenger,
});
assert.throws(
() =>
new ComposableObservableStore({
config: {
Example: exampleController,
},
}),
);
});
it('should throw if the controller messenger is omitted and updateStructure called with a BaseControllerV2 controller', function () {
const controllerMessenger = new ControllerMessenger();
const exampleController = new ExampleController({
messenger: controllerMessenger,
});
const store = new ComposableObservableStore({});
assert.throws(() => store.updateStructure({ Example: exampleController }));
});
it('should throw if initialized with undefined config entry', function () {
const controllerMessenger = new ControllerMessenger();
assert.throws(
() =>
new ComposableObservableStore({
config: {
Example: undefined,
},
controllerMessenger,
}),
);
});
});

View File

@ -1,8 +1,9 @@
import extension from 'extensionizer';
import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout';
import { SECOND } from '../../../../shared/constants/time';
import resolveEnsToIpfsContentId from './resolver';
const fetchWithTimeout = getFetchWithTimeout(30000);
const fetchWithTimeout = getFetchWithTimeout(SECOND * 30);
const supportedTopLevelDomains = ['eth'];

View File

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

View File

@ -1,4 +1,5 @@
import Analytics from 'analytics-node';
import { SECOND } from '../../../shared/constants/time';
const isDevOrTestEnvironment = Boolean(
process.env.METAMASK_DEBUG || process.env.IN_TEST,
@ -21,7 +22,7 @@ const SEGMENT_FLUSH_AT =
// deal with short lived sessions that happen faster than the interval
// e.g confirmations. This is set to 5,000ms (5 seconds) arbitrarily with the
// intent of having a value less than 10 seconds.
const SEGMENT_FLUSH_INTERVAL = 5000;
const SEGMENT_FLUSH_INTERVAL = SECOND * 5;
/**
* Creates a mock segment module for usage in test environments. This is used

View File

@ -20,6 +20,7 @@ import contractMap from '@metamask/contract-metadata';
import {
AddressBookController,
ApprovalController,
ControllerMessenger,
CurrencyRateController,
PhishingController,
NotificationController,
@ -28,6 +29,7 @@ import { TRANSACTION_STATUSES } from '../../shared/constants/transaction';
import { MAINNET_CHAIN_ID } from '../../shared/constants/network';
import { UI_NOTIFICATIONS } from '../../shared/notifications';
import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils';
import { MILLISECOND } from '../../shared/constants/time';
import ComposableObservableStore from './lib/ComposableObservableStore';
import AccountTracker from './lib/account-tracker';
@ -81,7 +83,10 @@ export default class MetamaskController extends EventEmitter {
this.defaultMaxListeners = 20;
this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200);
this.sendUpdate = debounce(
this.privateSendUpdate.bind(this),
MILLISECOND * 200,
);
this.opts = opts;
this.extension = opts.extension;
this.platform = opts.platform;
@ -96,8 +101,13 @@ export default class MetamaskController extends EventEmitter {
this.getRequestAccountTabIds = opts.getRequestAccountTabIds;
this.getOpenMetamaskTabsIds = opts.getOpenMetamaskTabsIds;
const controllerMessenger = new ControllerMessenger();
// observable state store
this.store = new ComposableObservableStore(initState);
this.store = new ComposableObservableStore({
state: initState,
controllerMessenger,
});
// external connections by origin
// Do not modify directly. Use the associated methods.
@ -157,10 +167,14 @@ export default class MetamaskController extends EventEmitter {
preferencesStore: this.preferencesController.store,
});
this.currencyRateController = new CurrencyRateController(
{ includeUSDRate: true },
initState.CurrencyController,
);
const currencyRateMessenger = controllerMessenger.getRestricted({
name: 'CurrencyRateController',
});
this.currencyRateController = new CurrencyRateController({
includeUSDRate: true,
messenger: currencyRateMessenger,
state: initState.CurrencyController,
});
this.phishingController = new PhishingController();
@ -222,10 +236,12 @@ export default class MetamaskController extends EventEmitter {
this.accountTracker.start();
this.incomingTransactionsController.start();
this.tokenRatesController.start();
this.currencyRateController.start();
} else {
this.accountTracker.stop();
this.incomingTransactionsController.stop();
this.tokenRatesController.stop();
this.currencyRateController.stop();
}
});
@ -364,18 +380,15 @@ export default class MetamaskController extends EventEmitter {
}
});
this.networkController.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, () => {
this.setCurrentCurrency(
this.currencyRateController.state.currentCurrency,
(error) => {
if (error) {
throw error;
}
},
);
this.networkController.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, async () => {
const { ticker } = this.networkController.getProviderConfig();
try {
await this.currencyRateController.setNativeCurrency(ticker);
} catch (error) {
// TODO: Handle failure to get conversion rate more gracefully
console.error(error);
}
});
const { ticker } = this.networkController.getProviderConfig();
this.currencyRateController.configure({ nativeCurrency: ticker ?? 'ETH' });
this.networkController.lookupNetwork();
this.messageManager = new MessageManager();
this.personalMessageManager = new PersonalMessageManager();
@ -439,33 +452,37 @@ export default class MetamaskController extends EventEmitter {
NotificationController: this.notificationController,
});
this.memStore = new ComposableObservableStore(null, {
AppStateController: this.appStateController.store,
NetworkController: this.networkController.store,
AccountTracker: this.accountTracker.store,
TxController: this.txController.memStore,
CachedBalancesController: this.cachedBalancesController.store,
TokenRatesController: this.tokenRatesController.store,
MessageManager: this.messageManager.memStore,
PersonalMessageManager: this.personalMessageManager.memStore,
DecryptMessageManager: this.decryptMessageManager.memStore,
EncryptionPublicKeyManager: this.encryptionPublicKeyManager.memStore,
TypesMessageManager: this.typedMessageManager.memStore,
KeyringController: this.keyringController.memStore,
PreferencesController: this.preferencesController.store,
MetaMetricsController: this.metaMetricsController.store,
AddressBookController: this.addressBookController,
CurrencyController: this.currencyRateController,
AlertController: this.alertController.store,
OnboardingController: this.onboardingController.store,
IncomingTransactionsController: this.incomingTransactionsController.store,
PermissionsController: this.permissionsController.permissions,
PermissionsMetadata: this.permissionsController.store,
ThreeBoxController: this.threeBoxController.store,
SwapsController: this.swapsController.store,
EnsController: this.ensController.store,
ApprovalController: this.approvalController,
NotificationController: this.notificationController,
this.memStore = new ComposableObservableStore({
config: {
AppStateController: this.appStateController.store,
NetworkController: this.networkController.store,
AccountTracker: this.accountTracker.store,
TxController: this.txController.memStore,
CachedBalancesController: this.cachedBalancesController.store,
TokenRatesController: this.tokenRatesController.store,
MessageManager: this.messageManager.memStore,
PersonalMessageManager: this.personalMessageManager.memStore,
DecryptMessageManager: this.decryptMessageManager.memStore,
EncryptionPublicKeyManager: this.encryptionPublicKeyManager.memStore,
TypesMessageManager: this.typedMessageManager.memStore,
KeyringController: this.keyringController.memStore,
PreferencesController: this.preferencesController.store,
MetaMetricsController: this.metaMetricsController.store,
AddressBookController: this.addressBookController,
CurrencyController: this.currencyRateController,
AlertController: this.alertController.store,
OnboardingController: this.onboardingController.store,
IncomingTransactionsController: this.incomingTransactionsController
.store,
PermissionsController: this.permissionsController.permissions,
PermissionsMetadata: this.permissionsController.store,
ThreeBoxController: this.threeBoxController.store,
SwapsController: this.swapsController.store,
EnsController: this.ensController.store,
ApprovalController: this.approvalController,
NotificationController: this.notificationController,
},
controllerMessenger,
});
this.memStore.subscribe(this.sendUpdate.bind(this));
@ -649,7 +666,11 @@ export default class MetamaskController extends EventEmitter {
return {
// etc
getState: (cb) => cb(null, this.getState()),
setCurrentCurrency: this.setCurrentCurrency.bind(this),
setCurrentCurrency: nodeify(
this.currencyRateController.setCurrentCurrency.bind(
this.currencyRateController,
),
),
setUseBlockie: this.setUseBlockie.bind(this),
setUseNonceField: this.setUseNonceField.bind(this),
setUsePhishDetect: this.setUsePhishDetect.bind(this),
@ -763,6 +784,14 @@ export default class MetamaskController extends EventEmitter {
this.appStateController.setConnectedStatusPopoverHasBeenShown,
this.appStateController,
),
setRecoveryPhraseReminderHasBeenShown: nodeify(
this.appStateController.setRecoveryPhraseReminderHasBeenShown,
this.appStateController,
),
setRecoveryPhraseReminderLastShown: nodeify(
this.appStateController.setRecoveryPhraseReminderLastShown,
this.appStateController,
),
// EnsController
tryReverseResolveAddress: nodeify(
@ -2511,29 +2540,6 @@ export default class MetamaskController extends EventEmitter {
// Log blocks
/**
* A method for setting the user's preferred display currency.
* @param {string} currencyCode - The code of the preferred currency.
* @param {Function} cb - A callback function returning currency info.
*/
setCurrentCurrency(currencyCode, cb) {
const { ticker } = this.networkController.getProviderConfig();
try {
const currencyState = {
nativeCurrency: ticker,
currentCurrency: currencyCode,
};
this.currencyRateController.update(currencyState);
this.currencyRateController.configure(currencyState);
cb(null);
return;
} catch (err) {
cb(err);
// eslint-disable-next-line no-useless-return
return;
}
}
/**
* A method for selecting a custom URL for an ethereum RPC provider and updating it
* @param {string} rpcUrl - A URL for a valid Ethereum RPC API.

View File

@ -654,46 +654,24 @@ describe('MetaMaskController', function () {
});
describe('#setCustomRpc', function () {
let rpcUrl;
beforeEach(function () {
rpcUrl = metamaskController.setCustomRpc(
it('returns custom RPC that when called', async function () {
const rpcUrl = await metamaskController.setCustomRpc(
CUSTOM_RPC_URL,
CUSTOM_RPC_CHAIN_ID,
);
assert.equal(rpcUrl, CUSTOM_RPC_URL);
});
it('returns custom RPC that when called', async function () {
assert.equal(await rpcUrl, CUSTOM_RPC_URL);
});
it('changes the network controller rpc', function () {
it('changes the network controller rpc', async function () {
await metamaskController.setCustomRpc(
CUSTOM_RPC_URL,
CUSTOM_RPC_CHAIN_ID,
);
const networkControllerState = metamaskController.networkController.store.getState();
assert.equal(networkControllerState.provider.rpcUrl, CUSTOM_RPC_URL);
});
});
describe('#setCurrentCurrency', function () {
let defaultMetaMaskCurrency;
beforeEach(function () {
defaultMetaMaskCurrency =
metamaskController.currencyRateController.state.currentCurrency;
});
it('defaults to usd', function () {
assert.equal(defaultMetaMaskCurrency, 'usd');
});
it('sets currency to JPY', function () {
metamaskController.setCurrentCurrency('JPY', noop);
assert.equal(
metamaskController.currencyRateController.state.currentCurrency,
'JPY',
);
});
});
describe('#addNewAccount', function () {
it('errors when an primary keyring is does not exist', async function () {
const addNewAccount = metamaskController.addNewAccount();

View File

@ -0,0 +1,32 @@
import { cloneDeep } from 'lodash';
const version = 61;
/**
* Initialize attributes related to recovery seed phrase reminder
*/
export default {
version,
async migrate(originalVersionedData) {
const versionedData = cloneDeep(originalVersionedData);
versionedData.meta.version = version;
const state = versionedData.data;
const newState = transformState(state);
versionedData.data = newState;
return versionedData;
},
};
function transformState(state) {
const currentTime = new Date().getTime();
if (state.AppStateController) {
state.AppStateController.recoveryPhraseReminderHasBeenShown = false;
state.AppStateController.recoveryPhraseReminderLastShown = currentTime;
} else {
state.AppStateController = {
recoveryPhraseReminderHasBeenShown: false,
recoveryPhraseReminderLastShown: currentTime,
};
}
return state;
}

View File

@ -0,0 +1,67 @@
import { strict as assert } from 'assert';
import sinon from 'sinon';
import migration61 from './061';
describe('migration #61', function () {
let dateStub;
beforeEach(function () {
dateStub = sinon.stub(Date.prototype, 'getTime').returns(1621580400000);
});
afterEach(function () {
dateStub.restore();
});
it('should update the version metadata', async function () {
const oldStorage = {
meta: {
version: 60,
},
data: {},
};
const newStorage = await migration61.migrate(oldStorage);
assert.deepEqual(newStorage.meta, {
version: 61,
});
});
it('should set recoveryPhraseReminderHasBeenShown to false and recoveryPhraseReminderLastShown to the current time', async function () {
const oldStorage = {
meta: {},
data: {
AppStateController: {
existingProperty: 'foo',
},
},
};
const newStorage = await migration61.migrate(oldStorage);
assert.deepEqual(newStorage.data, {
AppStateController: {
recoveryPhraseReminderHasBeenShown: false,
recoveryPhraseReminderLastShown: 1621580400000,
existingProperty: 'foo',
},
});
});
it('should initialize AppStateController if it does not exist', async function () {
const oldStorage = {
meta: {},
data: {
existingProperty: 'foo',
},
};
const newStorage = await migration61.migrate(oldStorage);
assert.deepEqual(newStorage.data, {
existingProperty: 'foo',
AppStateController: {
recoveryPhraseReminderHasBeenShown: false,
recoveryPhraseReminderLastShown: 1621580400000,
},
});
});
});

View File

@ -65,6 +65,7 @@ const migrations = [
require('./058').default,
require('./059').default,
require('./060').default,
require('./061').default,
];
export default migrations;

View File

@ -1,8 +1,8 @@
import extension from 'extensionizer';
import { getBlockExplorerLink } from '@metamask/etherscan-link';
import { getEnvironmentType, checkForError } from '../lib/util';
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app';
import { TRANSACTION_STATUSES } from '../../../shared/constants/transaction';
import { getBlockExplorerUrlForTx } from '../../../shared/modules/transaction.utils';
export default class ExtensionPlatform {
//
@ -192,7 +192,7 @@ export default class ExtensionPlatform {
_showConfirmedTransaction(txMeta, rpcPrefs) {
this._subscribeToNotificationClicked();
const url = getBlockExplorerUrlForTx(txMeta, rpcPrefs);
const url = getBlockExplorerLink(txMeta, rpcPrefs);
const nonce = parseInt(txMeta.txParams.nonce, 16);
const title = 'Confirmed transaction';

View File

@ -0,0 +1,16 @@
/**
* Exit the process with an error message.
*
* Note that this should be called before the process ends, but it will not
* itself end the process. This is because the Node.js documentation strongly
* advises against calling `process.exit` directly.
*
* @param {string} errorMessage - The error message that is causing the non-
* zero exit code.
*/
function exitWithError(errorMessage) {
console.error(errorMessage);
process.exitCode = 1;
}
module.exports = { exitWithError };

25
development/lib/retry.js Normal file
View File

@ -0,0 +1,25 @@
const { exitWithError } = require('./exit-with-error');
/**
* Run the given function, retrying it upon failure until reaching the
* specified number of retries.
*
* @param {number} retries - The number of retries upon failure to attempt.
* @param {function} functionToRetry - The function that will be retried upon failure.
*/
async function retry(retries, functionToRetry) {
let attempts = 0;
while (attempts <= retries) {
try {
await functionToRetry();
return;
} catch (error) {
console.error(error);
} finally {
attempts += 1;
}
}
exitWithError('Retry limit reached');
}
module.exports = { retry };

View File

@ -0,0 +1,134 @@
const spawn = require('cross-spawn');
/**
* Run a command to completion using the system shell.
*
* This will run a command with the specified arguments, and resolve when the
* process has exited. The STDOUT stream is monitored for output, which is
* returned after being split into lines. All output is expected to be UTF-8
* encoded, and empty lines are removed from the output.
*
* Anything received on STDERR is assumed to indicate a problem, and is tracked
* as an error.
*
* @param {string} command - The command to run
* @param {Array<string>} [args] - The arguments to pass to the command
* @returns {Array<string>} Lines of output received via STDOUT
*/
async function runCommand(command, args) {
const output = [];
let mostRecentError;
let errorSignal;
let errorCode;
const internalError = new Error('Internal');
try {
await new Promise((resolve, reject) => {
const childProcess = spawn(command, args, { encoding: 'utf8' });
childProcess.stdout.setEncoding('utf8');
childProcess.stderr.setEncoding('utf8');
childProcess.on('error', (error) => {
mostRecentError = error;
});
childProcess.stdout.on('data', (message) => {
const nonEmptyLines = message.split('\n').filter((line) => line !== '');
output.push(...nonEmptyLines);
});
childProcess.stderr.on('data', (message) => {
mostRecentError = new Error(message.trim());
});
childProcess.once('exit', (code, signal) => {
if (code === 0) {
return resolve();
}
errorCode = code;
errorSignal = signal;
return reject(internalError);
});
});
} catch (error) {
/**
* The error is re-thrown here in an `async` context to preserve the stack trace. If this was
* was thrown inside the Promise constructor, the stack trace would show a few frames of
* Node.js internals then end, without indicating where `runCommand` was called.
*/
if (error === internalError) {
let errorMessage;
if (errorCode !== null && errorSignal !== null) {
errorMessage = `Terminated by signal '${errorSignal}'; exited with code '${errorCode}'`;
} else if (errorSignal !== null) {
errorMessage = `Terminaled by signal '${errorSignal}'`;
} else if (errorCode === null) {
errorMessage = 'Exited with no code or signal';
} else {
errorMessage = `Exited with code '${errorCode}'`;
}
const improvedError = new Error(errorMessage);
if (mostRecentError) {
improvedError.cause = mostRecentError;
}
throw improvedError;
}
}
return output;
}
/**
* Run a command to using the system shell.
*
* This will run a command with the specified arguments, and resolve when the
* process has exited. The STDIN, STDOUT and STDERR streams are inherited,
* letting the command take over completely until it completes. The success or
* failure of the process is determined entirely by the exit code; STDERR
* output is not used to indicate failure.
*
* @param {string} command - The command to run
* @param {Array<string>} [args] - The arguments to pass to the command
*/
async function runInShell(command, args) {
let errorSignal;
let errorCode;
const internalError = new Error('Internal');
try {
await new Promise((resolve, reject) => {
const childProcess = spawn(command, args, {
encoding: 'utf8',
stdio: 'inherit',
});
childProcess.once('exit', (code, signal) => {
if (code === 0) {
return resolve();
}
errorCode = code;
errorSignal = signal;
return reject(internalError);
});
});
} catch (error) {
/**
* The error is re-thrown here in an `async` context to preserve the stack trace. If this was
* was thrown inside the Promise constructor, the stack trace would show a few frames of
* Node.js internals then end, without indicating where `runInShell` was called.
*/
if (error === internalError) {
let errorMessage;
if (errorCode !== null && errorSignal !== null) {
errorMessage = `Terminated by signal '${errorSignal}'; exited with code '${errorCode}'`;
} else if (errorSignal !== null) {
errorMessage = `Terminaled by signal '${errorSignal}'`;
} else if (errorCode === null) {
errorMessage = 'Exited with no code or signal';
} else {
errorMessage = `Exited with code '${errorCode}'`;
}
const improvedError = new Error(errorMessage);
throw improvedError;
}
}
}
module.exports = { runCommand, runInShell };

View File

@ -1,9 +1,6 @@
#!/usr/bin/env node
const childProcess = require('child_process');
const pify = require('pify');
const exec = pify(childProcess.exec, { multiArgs: true });
const VERSION = require('../dist/chrome/manifest.json').version; // eslint-disable-line import/no-unresolved
const { runCommand, runInShell } = require('./lib/run-command');
start().catch((error) => {
console.error(error);
@ -31,11 +28,17 @@ async function start() {
} else {
// create sentry release
console.log(`creating Sentry release for "${VERSION}"...`);
await exec(`sentry-cli releases new ${VERSION}`);
await runCommand('sentry-cli', ['releases', 'new', VERSION]);
console.log(
`removing any existing files from Sentry release "${VERSION}"...`,
);
await exec(`sentry-cli releases files ${VERSION} delete --all`);
await runCommand('sentry-cli', [
'releases',
'files',
VERSION,
'delete',
'--all',
]);
}
// check if version has artifacts or not
@ -49,34 +52,43 @@ async function start() {
}
// upload sentry source and sourcemaps
await exec(`./development/sentry-upload-artifacts.sh --release ${VERSION}`);
await runInShell('./development/sentry-upload-artifacts.sh', [
'--release',
VERSION,
]);
}
async function checkIfAuthWorks() {
const itWorked = await doesNotFail(async () => {
await exec(`sentry-cli releases list`);
});
return itWorked;
return await doesNotFail(() =>
runCommand('sentry-cli', ['releases', 'list']),
);
}
async function checkIfVersionExists() {
const versionAlreadyExists = await doesNotFail(async () => {
await exec(`sentry-cli releases info ${VERSION}`);
});
return versionAlreadyExists;
return await doesNotFail(() =>
runCommand('sentry-cli', ['releases', 'info', VERSION]),
);
}
async function checkIfVersionHasArtifacts() {
const artifacts = await exec(`sentry-cli releases files ${VERSION} list`);
const [artifact] = await runCommand('sentry-cli', [
'releases',
'files',
VERSION,
'list',
]);
// When there's no artifacts, we get a response from the shell like this ['', '']
return artifacts[0] && artifacts[0].length > 0;
return artifact?.length > 0;
}
async function doesNotFail(asyncFn) {
try {
await asyncFn();
return true;
} catch (err) {
return false;
} catch (error) {
if (error.message === `Exited with code '1'`) {
return false;
}
throw error;
}
}

View File

@ -1,6 +1,5 @@
#!/usr/bin/env bash
set -x
set -e
set -u
set -o pipefail

View File

@ -6,7 +6,7 @@ module.exports = {
coverageThreshold: {
global: {
branches: 32.75,
functions: 43.31,
functions: 42.9,
lines: 43.12,
statements: 43.67,
},

View File

@ -1,14 +1,7 @@
{
"exclude": [
"*.log",
"builds",
"coverage",
"dist",
"docs",
"lavamoat",
"node:console",
"node_modules",
"patches",
"test-artifacts"
]
"compilerOptions": {
"target": "ES6",
"module": "commonjs"
},
"include": ["ui/**/*.js", "app/**/*.js", "shared/**/*.js"]
}

View File

@ -223,6 +223,11 @@
"js-tokens": true
}
},
"@babel/parser": {
"globals": {
"BigInt": true
}
},
"@babel/plugin-proposal-async-generator-functions": {
"packages": {
"@babel/core": true,
@ -869,6 +874,7 @@
},
"acorn": {
"globals": {
"BigInt": true,
"define": true
}
},
@ -878,6 +884,9 @@
}
},
"acorn-node": {
"globals": {
"BigInt": true
},
"packages": {
"acorn": true,
"acorn-dynamic-import": true,
@ -950,6 +959,16 @@
"buffer-equal": true
}
},
"are-we-there-yet": {
"builtin": {
"events.EventEmitter": true,
"util.inherits": true
},
"packages": {
"delegates": true,
"readable-stream": true
}
},
"arr-diff": {
"packages": {
"arr-flatten": true,
@ -1302,6 +1321,7 @@
"anymatch": true,
"async-each": true,
"braces": true,
"fsevents": true,
"glob-parent": true,
"inherits": true,
"is-binary-path": true,
@ -1553,6 +1573,16 @@
"through2": true
}
},
"detect-libc": {
"builtin": {
"child_process.spawnSync": true,
"fs.readdirSync": true,
"os.platform": true
},
"globals": {
"process.env": true
}
},
"detective": {
"packages": {
"acorn-node": true,
@ -1640,7 +1670,10 @@
"es-abstract": {
"globals": {
"AggregateError": true,
"Atomics": true,
"BigInt": true,
"FinalizationRegistry": true,
"SharedArrayBuffer": true,
"WeakRef": true
},
"packages": {
@ -1993,6 +2026,45 @@
"process.version": true
}
},
"fsevents": {
"builtin": {
"events.EventEmitter": true,
"fs.stat": true,
"path.join": true,
"util.inherits": true
},
"globals": {
"__dirname": true,
"process.nextTick": true,
"process.platform": true,
"setImmediate": true
},
"native": true,
"packages": {
"node-pre-gyp": true
}
},
"gauge": {
"builtin": {
"util.format": true
},
"globals": {
"clearInterval": true,
"process": true,
"setImmediate": true,
"setInterval": true
},
"packages": {
"aproba": true,
"console-control-strings": true,
"has-unicode": true,
"object-assign": true,
"signal-exit": true,
"string-width": true,
"strip-ansi": true,
"wide-align": true
}
},
"get-assigned-identifiers": {
"builtin": {
"assert.equal": true
@ -2373,6 +2445,16 @@
"process.argv": true
}
},
"has-unicode": {
"builtin": {
"os.type": true
},
"globals": {
"process.env.LANG": true,
"process.env.LC_ALL": true,
"process.env.LC_CTYPE": true
}
},
"has-value": {
"packages": {
"get-value": true,
@ -2526,6 +2608,11 @@
"is-plain-object": true
}
},
"is-fullwidth-code-point": {
"packages": {
"number-is-nan": true
}
},
"is-glob": {
"packages": {
"is-extglob": true
@ -2910,6 +2997,56 @@
"setTimeout": true
}
},
"node-pre-gyp": {
"builtin": {
"events.EventEmitter": true,
"fs.existsSync": true,
"fs.readFileSync": true,
"fs.renameSync": true,
"path.dirname": true,
"path.existsSync": true,
"path.join": true,
"path.resolve": true,
"url.parse": true,
"url.resolve": true,
"util.inherits": true
},
"globals": {
"__dirname": true,
"console.log": true,
"process.arch": true,
"process.cwd": true,
"process.env": true,
"process.platform": true,
"process.version.substr": true,
"process.versions": true
},
"packages": {
"detect-libc": true,
"nopt": true,
"npmlog": true,
"rimraf": true,
"semver": true
}
},
"nopt": {
"builtin": {
"path": true,
"stream.Stream": true,
"url": true
},
"globals": {
"console": true,
"process.argv": true,
"process.env.DEBUG_NOPT": true,
"process.env.NOPT_DEBUG": true,
"process.platform": true
},
"packages": {
"abbrev": true,
"osenv": true
}
},
"normalize-path": {
"packages": {
"remove-trailing-separator": true
@ -2925,6 +3062,22 @@
"once": true
}
},
"npmlog": {
"builtin": {
"events.EventEmitter": true,
"util": true
},
"globals": {
"process.nextTick": true,
"process.stderr": true
},
"packages": {
"are-we-there-yet": true,
"console-control-strings": true,
"gauge": true,
"set-blocking": true
}
},
"object-copy": {
"packages": {
"copy-descriptor": true,
@ -2937,6 +3090,7 @@
"util.inspect": true
},
"globals": {
"BigInt": true,
"HTMLElement": true
}
},
@ -2991,6 +3145,54 @@
"readable-stream": true
}
},
"os-homedir": {
"builtin": {
"os.homedir": true
},
"globals": {
"process.env": true,
"process.getuid": true,
"process.platform": true
}
},
"os-tmpdir": {
"globals": {
"process.env.SystemRoot": true,
"process.env.TEMP": true,
"process.env.TMP": true,
"process.env.TMPDIR": true,
"process.env.windir": true,
"process.platform": true
}
},
"osenv": {
"builtin": {
"child_process.exec": true,
"path": true
},
"globals": {
"process.env.COMPUTERNAME": true,
"process.env.ComSpec": true,
"process.env.EDITOR": true,
"process.env.HOSTNAME": true,
"process.env.PATH": true,
"process.env.PROMPT": true,
"process.env.PS1": true,
"process.env.Path": true,
"process.env.SHELL": true,
"process.env.USER": true,
"process.env.USERDOMAIN": true,
"process.env.USERNAME": true,
"process.env.VISUAL": true,
"process.env.path": true,
"process.nextTick": true,
"process.platform": true
},
"packages": {
"os-homedir": true,
"os-tmpdir": true
}
},
"parent-module": {
"packages": {
"callsites": true
@ -3573,6 +3775,12 @@
"process": true
}
},
"set-blocking": {
"globals": {
"process.stderr": true,
"process.stdout": true
}
},
"set-value": {
"packages": {
"extend-shallow": true,
@ -3766,6 +3974,7 @@
},
"string-width": {
"packages": {
"code-point-at": true,
"emoji-regex": true,
"is-fullwidth-code-point": true,
"strip-ansi": true
@ -4322,6 +4531,11 @@
"isexe": true
}
},
"wide-align": {
"packages": {
"string-width": true
}
},
"write": {
"builtin": {
"fs.createWriteStream": true,

View File

@ -4,7 +4,7 @@
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/metamask-extension"
"url": "https://github.com/MetaMask/metamask-extension.git"
},
"scripts": {
"setup": "yarn install && yarn setup:postinstall",
@ -32,11 +32,12 @@
"test:unit:lax": "mocha --exit --require test/env.js --require test/setup.js --ignore './app/scripts/controllers/permissions/*.test.js' --recursive './{app,shared}/**/*.test.js'",
"test:unit:strict": "mocha --exit --require test/env.js --require test/setup.js --recursive './app/scripts/controllers/permissions/*.test.js'",
"test:unit:path": "mocha --exit --require test/env.js --require test/setup.js --recursive",
"test:e2e:chrome": "SELENIUM_BROWSER=chrome test/e2e/run-all.sh",
"test:e2e:chrome:metrics": "SELENIUM_BROWSER=chrome mocha test/e2e/metrics.spec.js",
"test:e2e:firefox": "SELENIUM_BROWSER=firefox test/e2e/run-all.sh",
"test:e2e:firefox:metrics": "SELENIUM_BROWSER=firefox mocha test/e2e/metrics.spec.js",
"test:e2e:chrome": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js",
"test:e2e:chrome:metrics": "SELENIUM_BROWSER=chrome node test/e2e/run-e2e-test.js test/e2e/metrics.spec.js",
"test:e2e:firefox": "SELENIUM_BROWSER=firefox node test/e2e/run-all.js",
"test:e2e:firefox:metrics": "SELENIUM_BROWSER=firefox node test/e2e/run-e2e-test.js test/e2e/metrics.spec.js",
"test:coverage": "nyc --silent --check-coverage yarn test:unit:strict && nyc --silent --no-clean yarn test:unit:lax && nyc report --reporter=text --reporter=html",
"test:e2e:single": "node test/e2e/run-e2e-test.js",
"test:coverage:jest": "jest --coverage --maxWorkers=2",
"test:coverage:strict": "nyc --check-coverage yarn test:unit:strict",
"test:coverage:path": "nyc --check-coverage yarn test:unit:path",
@ -96,9 +97,9 @@
"@fortawesome/fontawesome-free": "^5.13.0",
"@lavamoat/preinstall-always-fail": "^1.0.0",
"@material-ui/core": "^4.11.0",
"@metamask/contract-metadata": "^1.22.0",
"@metamask/controllers": "^8.0.0",
"@metamask/eth-ledger-bridge-keyring": "^0.5.0",
"@metamask/contract-metadata": "^1.26.0",
"@metamask/controllers": "^9.0.0",
"@metamask/eth-ledger-bridge-keyring": "^0.6.0",
"@metamask/eth-token-tracker": "^3.0.1",
"@metamask/etherscan-link": "^2.1.0",
"@metamask/jazzicon": "^2.0.0",
@ -134,11 +135,11 @@
"eth-query": "^2.1.2",
"eth-rpc-errors": "^4.0.2",
"eth-sig-util": "^3.0.0",
"eth-trezor-keyring": "^0.6.0",
"eth-trezor-keyring": "^0.7.0",
"ethereum-ens-network-map": "^1.0.2",
"ethereumjs-abi": "^0.6.4",
"ethereumjs-tx": "1.3.7",
"ethereumjs-util": "^7.0.9",
"ethereumjs-util": "^7.0.10",
"ethereumjs-wallet": "^0.6.4",
"ethers": "^5.0.8",
"ethjs": "^0.4.0",
@ -212,7 +213,7 @@
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.5.5",
"@lavamoat/allow-scripts": "^1.0.6",
"@metamask/auto-changelog": "^1.0.0",
"@metamask/auto-changelog": "^2.1.0",
"@metamask/eslint-config": "^6.0.0",
"@metamask/eslint-config-jest": "^6.0.0",
"@metamask/eslint-config-mocha": "^6.0.0",
@ -272,6 +273,7 @@
"gulp-terser-js": "^5.2.2",
"gulp-watch": "^5.0.1",
"gulp-zip": "^4.0.0",
"history": "^5.0.0",
"jest": "^26.6.3",
"jsdom": "^11.2.0",
"koa": "^2.7.0",
@ -311,7 +313,8 @@
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"watchify": "^3.11.1",
"webpack": "^4.41.6"
"webpack": "^4.41.6",
"yargs": "^17.0.1"
},
"engines": {
"node": "^14.15.1",
@ -336,7 +339,8 @@
"gc-stats": false,
"github:assemblyscript/assemblyscript": false,
"tiny-secp256k1": false,
"@lavamoat/preinstall-always-fail": false
"@lavamoat/preinstall-always-fail": false,
"fsevents": false
}
}
}

View File

@ -0,0 +1,19 @@
diff --git a/node_modules/selenium-webdriver/chromium.js b/node_modules/selenium-webdriver/chromium.js
index d828ce5..87176f4 100644
--- a/node_modules/selenium-webdriver/chromium.js
+++ b/node_modules/selenium-webdriver/chromium.js
@@ -197,6 +197,14 @@ class ServiceBuilder extends remote.DriverService.Builder {
return this.addArguments('--log-path=' + path);
}
+ /**
+ * Enables Chrome logging.
+ * @returns {!ServiceBuilder} A self reference.
+ */
+ enableChromeLogging() {
+ return this.addArguments('--enable-chrome-logs');
+ }
+
/**
* Enables verbose logging.
* @return {!ServiceBuilder} A self reference.

11
shared/constants/gas.js Normal file
View File

@ -0,0 +1,11 @@
import { addHexPrefix } from 'ethereumjs-util';
const TWENTY_ONE_THOUSAND = 21000;
const ONE_HUNDRED_THOUSAND = 100000;
export const GAS_LIMITS = {
// maximum gasLimit of a simple send
SIMPLE: addHexPrefix(TWENTY_ONE_THOUSAND.toString(16)),
// a base estimate for token transfers.
BASE_TOKEN_ESTIMATE: addHexPrefix(ONE_HUNDRED_THOUSAND.toString(16)),
};

View File

@ -63,6 +63,7 @@ const SWAPS_TESTNET_CHAIN_ID = '0x539';
const SWAPS_TESTNET_HOST = 'https://metaswap-api.airswap-dev.codefi.network';
const BSC_DEFAULT_BLOCK_EXPLORER_URL = 'https://bscscan.com/';
const MAINNET_DEFAULT_BLOCK_EXPLORER_URL = 'https://etherscan.io/';
export const ALLOWED_SWAPS_CHAIN_IDS = {
[MAINNET_CHAIN_ID]: true,
@ -90,4 +91,5 @@ export const SWAPS_CHAINID_DEFAULT_TOKEN_MAP = {
export const SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP = {
[BSC_CHAIN_ID]: BSC_DEFAULT_BLOCK_EXPLORER_URL,
[MAINNET_CHAIN_ID]: MAINNET_DEFAULT_BLOCK_EXPLORER_URL,
};

5
shared/constants/time.js Normal file
View File

@ -0,0 +1,5 @@
export const MILLISECOND = 1;
export const SECOND = MILLISECOND * 1000;
export const MINUTE = SECOND * 60;
export const HOUR = MINUTE * 60;
export const DAY = HOUR * 24;

View File

@ -1,13 +1,14 @@
import { strict as assert } from 'assert';
import nock from 'nock';
import { MILLISECOND, SECOND } from '../constants/time';
import getFetchWithTimeout from './fetch-with-timeout';
describe('getFetchWithTimeout', function () {
it('fetches a url', async function () {
nock('https://api.infura.io').get('/money').reply(200, '{"hodl": false}');
const fetchWithTimeout = getFetchWithTimeout(30000);
const fetchWithTimeout = getFetchWithTimeout(SECOND * 30);
const response = await (
await fetchWithTimeout('https://api.infura.io/money')
).json();
@ -19,10 +20,10 @@ describe('getFetchWithTimeout', function () {
it('throws when the request hits a custom timeout', async function () {
nock('https://api.infura.io')
.get('/moon')
.delay(2000)
.delay(SECOND * 2)
.reply(200, '{"moon": "2012-12-21T11:11:11Z"}');
const fetchWithTimeout = getFetchWithTimeout(123);
const fetchWithTimeout = getFetchWithTimeout(MILLISECOND * 123);
try {
await fetchWithTimeout('https://api.infura.io/moon').then((r) =>
@ -37,10 +38,10 @@ describe('getFetchWithTimeout', function () {
it('should abort the request when the custom timeout is hit', async function () {
nock('https://api.infura.io')
.get('/moon')
.delay(2000)
.delay(SECOND * 2)
.reply(200, '{"moon": "2012-12-21T11:11:11Z"}');
const fetchWithTimeout = getFetchWithTimeout(123);
const fetchWithTimeout = getFetchWithTimeout(MILLISECOND * 123);
try {
await fetchWithTimeout('https://api.infura.io/moon').then((r) =>

View File

@ -4,9 +4,10 @@ import {
isValidChecksumAddress,
addHexPrefix,
toChecksumAddress,
zeroAddress,
} from 'ethereumjs-util';
export const BURN_ADDRESS = '0x0000000000000000000000000000000000000000';
export const BURN_ADDRESS = zeroAddress();
export function isBurnAddress(address) {
return address === BURN_ADDRESS;

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