mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
Merge remote-tracking branch 'upstream/develop' into minimal
This commit is contained in:
commit
edd6d489ba
@ -266,7 +266,8 @@ module.exports = {
|
|||||||
'**/__snapshots__/*.snap',
|
'**/__snapshots__/*.snap',
|
||||||
'app/scripts/controllers/app-state.test.js',
|
'app/scripts/controllers/app-state.test.js',
|
||||||
'app/scripts/controllers/network/**/*.test.js',
|
'app/scripts/controllers/network/**/*.test.js',
|
||||||
'app/scripts/controllers/network/provider-api-tests/*.js',
|
'app/scripts/controllers/network/**/*.test.ts',
|
||||||
|
'app/scripts/controllers/network/provider-api-tests/*.ts',
|
||||||
'app/scripts/controllers/permissions/**/*.test.js',
|
'app/scripts/controllers/permissions/**/*.test.js',
|
||||||
'app/scripts/lib/**/*.test.js',
|
'app/scripts/lib/**/*.test.js',
|
||||||
'app/scripts/migrations/*.test.js',
|
'app/scripts/migrations/*.test.js',
|
||||||
|
@ -606,13 +606,6 @@ const state = {
|
|||||||
rpcUrl: '',
|
rpcUrl: '',
|
||||||
chainId: '0x5',
|
chainId: '0x5',
|
||||||
},
|
},
|
||||||
previousProviderStore: {
|
|
||||||
type: 'goerli',
|
|
||||||
ticker: 'ETH',
|
|
||||||
nickname: '',
|
|
||||||
rpcUrl: '',
|
|
||||||
chainId: '0x5',
|
|
||||||
},
|
|
||||||
network: '5',
|
network: '5',
|
||||||
accounts: {
|
accounts: {
|
||||||
'0x64a845a5b02460acf8a3d84503b0d68d028b4bb4': {
|
'0x64a845a5b02460acf8a3d84503b0d68d028b4bb4': {
|
||||||
|
52
app/_locales/en/messages.json
generated
52
app/_locales/en/messages.json
generated
@ -109,6 +109,9 @@
|
|||||||
"about": {
|
"about": {
|
||||||
"message": "About"
|
"message": "About"
|
||||||
},
|
},
|
||||||
|
"accept": {
|
||||||
|
"message": "Accept"
|
||||||
|
},
|
||||||
"acceptTermsOfUse": {
|
"acceptTermsOfUse": {
|
||||||
"message": "I have read and agree to the $1",
|
"message": "I have read and agree to the $1",
|
||||||
"description": "$1 is the `terms` message"
|
"description": "$1 is the `terms` message"
|
||||||
@ -305,6 +308,10 @@
|
|||||||
"advancedPriorityFeeToolTip": {
|
"advancedPriorityFeeToolTip": {
|
||||||
"message": "Priority fee (aka “miner tip”) goes directly to miners and incentivizes them to prioritize your transaction."
|
"message": "Priority fee (aka “miner tip”) goes directly to miners and incentivizes them to prioritize your transaction."
|
||||||
},
|
},
|
||||||
|
"agreeTermsOfUse": {
|
||||||
|
"message": "I agree to Metamask's $1",
|
||||||
|
"description": "$1 is the `terms` link"
|
||||||
|
},
|
||||||
"airgapVault": {
|
"airgapVault": {
|
||||||
"message": "AirGap Vault"
|
"message": "AirGap Vault"
|
||||||
},
|
},
|
||||||
@ -815,7 +822,7 @@
|
|||||||
"message": "Contract deployment"
|
"message": "Contract deployment"
|
||||||
},
|
},
|
||||||
"contractDescription": {
|
"contractDescription": {
|
||||||
"message": "To protect yourself against scammers, take a moment to verify contract details."
|
"message": "To protect yourself against scammers, take a moment to verify third-party details."
|
||||||
},
|
},
|
||||||
"contractInteraction": {
|
"contractInteraction": {
|
||||||
"message": "Contract interaction"
|
"message": "Contract interaction"
|
||||||
@ -830,10 +837,10 @@
|
|||||||
"message": "Contract requesting signature"
|
"message": "Contract requesting signature"
|
||||||
},
|
},
|
||||||
"contractRequestingSpendingCap": {
|
"contractRequestingSpendingCap": {
|
||||||
"message": "Contract requesting spending cap"
|
"message": "Third party requesting spending cap"
|
||||||
},
|
},
|
||||||
"contractTitle": {
|
"contractTitle": {
|
||||||
"message": "Contract details"
|
"message": "Third-party details"
|
||||||
},
|
},
|
||||||
"contractToken": {
|
"contractToken": {
|
||||||
"message": "Token contract"
|
"message": "Token contract"
|
||||||
@ -929,6 +936,21 @@
|
|||||||
"custodianAccount": {
|
"custodianAccount": {
|
||||||
"message": "Custodian account"
|
"message": "Custodian account"
|
||||||
},
|
},
|
||||||
|
"custodyRefreshTokenModalDescription": {
|
||||||
|
"message": "Please go to $1 and click the 'Connect to MMI' button within their user interface to connect your accounts to MMI again."
|
||||||
|
},
|
||||||
|
"custodyRefreshTokenModalDescription1": {
|
||||||
|
"message": "Your custodian issues a token that authenticates the MetaMask Institutional extension, allowing you to connect your accounts."
|
||||||
|
},
|
||||||
|
"custodyRefreshTokenModalDescription2": {
|
||||||
|
"message": "This token expires after a certain period for security reasons. This requires you to reconnect to MMI."
|
||||||
|
},
|
||||||
|
"custodyRefreshTokenModalSubtitle": {
|
||||||
|
"message": "Why am I seeing this?"
|
||||||
|
},
|
||||||
|
"custodyRefreshTokenModalTitle": {
|
||||||
|
"message": "Your custodian session has expired"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"message": "Advanced"
|
"message": "Advanced"
|
||||||
},
|
},
|
||||||
@ -1808,14 +1830,14 @@
|
|||||||
"message": "Your initial transaction was confirmed by the network. Click OK to go back."
|
"message": "Your initial transaction was confirmed by the network. Click OK to go back."
|
||||||
},
|
},
|
||||||
"inputLogicEmptyState": {
|
"inputLogicEmptyState": {
|
||||||
"message": "Only enter a number that you're comfortable with the contract spending now or in the future. You can always increase the spending cap later."
|
"message": "Only enter a number that you're comfortable with the third party spending now or in the future. You can always increase the spending cap later."
|
||||||
},
|
},
|
||||||
"inputLogicEqualOrSmallerNumber": {
|
"inputLogicEqualOrSmallerNumber": {
|
||||||
"message": "This allows the contract to spend $1 from your current balance.",
|
"message": "This allows the third party to spend $1 from your current balance.",
|
||||||
"description": "$1 is the current token balance in the account and the name of the current token"
|
"description": "$1 is the current token balance in the account and the name of the current token"
|
||||||
},
|
},
|
||||||
"inputLogicHigherNumber": {
|
"inputLogicHigherNumber": {
|
||||||
"message": "This allows the contract to spend all your token balance until it reaches the cap or you revoke the spending cap. If this is not intended, consider setting a lower spending cap."
|
"message": "This allows the third party to spend all your token balance until it reaches the cap or you revoke the spending cap. If this is not intended, consider setting a lower spending cap."
|
||||||
},
|
},
|
||||||
"insightsFromSnap": {
|
"insightsFromSnap": {
|
||||||
"message": "Insights from $1",
|
"message": "Insights from $1",
|
||||||
@ -2486,6 +2508,9 @@
|
|||||||
"message": "OpenSea is the first provider for this feature. More providers coming soon!",
|
"message": "OpenSea is the first provider for this feature. More providers coming soon!",
|
||||||
"description": "Description of a notification in the 'See What's New' popup. Describes Opensea Security Provider feature."
|
"description": "Description of a notification in the 'See What's New' popup. Describes Opensea Security Provider feature."
|
||||||
},
|
},
|
||||||
|
"notifications18Title": {
|
||||||
|
"message": "Stay safe with security alerts"
|
||||||
|
},
|
||||||
"notifications19ActionText": {
|
"notifications19ActionText": {
|
||||||
"message": "Enable NFT autodetection"
|
"message": "Enable NFT autodetection"
|
||||||
},
|
},
|
||||||
@ -3328,7 +3353,7 @@
|
|||||||
"description": "$1 is a token symbol"
|
"description": "$1 is a token symbol"
|
||||||
},
|
},
|
||||||
"revokeSpendingCapTooltipText": {
|
"revokeSpendingCapTooltipText": {
|
||||||
"message": "This contract will be unable to spend any more of your current or future tokens."
|
"message": "This third party will be unable to spend any more of your current or future tokens."
|
||||||
},
|
},
|
||||||
"rpcUrl": {
|
"rpcUrl": {
|
||||||
"message": "New RPC URL"
|
"message": "New RPC URL"
|
||||||
@ -4292,6 +4317,15 @@
|
|||||||
"termsOfUse": {
|
"termsOfUse": {
|
||||||
"message": "terms of use"
|
"message": "terms of use"
|
||||||
},
|
},
|
||||||
|
"termsOfUseAgreeText": {
|
||||||
|
"message": " I agree to the Terms of Use, which apply to my use of MetaMask and all of its features"
|
||||||
|
},
|
||||||
|
"termsOfUseFooterText": {
|
||||||
|
"message": "Please scroll to read all sections"
|
||||||
|
},
|
||||||
|
"termsOfUseTitle": {
|
||||||
|
"message": "Our Terms of Use have updated"
|
||||||
|
},
|
||||||
"testNetworks": {
|
"testNetworks": {
|
||||||
"message": "Test networks"
|
"message": "Test networks"
|
||||||
},
|
},
|
||||||
@ -4660,7 +4694,7 @@
|
|||||||
"message": "Username"
|
"message": "Username"
|
||||||
},
|
},
|
||||||
"verifyContractDetails": {
|
"verifyContractDetails": {
|
||||||
"message": "Verify contract details"
|
"message": "Verify third-party details"
|
||||||
},
|
},
|
||||||
"verifyThisTokenDecimalOn": {
|
"verifyThisTokenDecimalOn": {
|
||||||
"message": "Token decimal can be found on $1",
|
"message": "Token decimal can be found on $1",
|
||||||
@ -4746,7 +4780,7 @@
|
|||||||
"message": "Warning"
|
"message": "Warning"
|
||||||
},
|
},
|
||||||
"warningTooltipText": {
|
"warningTooltipText": {
|
||||||
"message": "$1 The contract could spend your entire token balance without further notice or consent. Protect yourself by customizing a lower spending cap.",
|
"message": "$1 The third party could spend your entire token balance without further notice or consent. Protect yourself by customizing a lower spending cap.",
|
||||||
"description": "$1 is a warning icon with text 'Be careful' in 'warning' colour"
|
"description": "$1 is a warning icon with text 'Be careful' in 'warning' colour"
|
||||||
},
|
},
|
||||||
"weak": {
|
"weak": {
|
||||||
|
@ -80,7 +80,6 @@ log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'info');
|
|||||||
|
|
||||||
const platform = new ExtensionPlatform();
|
const platform = new ExtensionPlatform();
|
||||||
const notificationManager = new NotificationManager();
|
const notificationManager = new NotificationManager();
|
||||||
global.METAMASK_NOTIFIER = notificationManager;
|
|
||||||
|
|
||||||
let popupIsOpen = false;
|
let popupIsOpen = false;
|
||||||
let notificationIsOpen = false;
|
let notificationIsOpen = false;
|
||||||
@ -209,6 +208,7 @@ browser.runtime.onConnectExternal.addListener(async (...args) => {
|
|||||||
* @property {boolean} isInitialized - Whether the first vault has been created.
|
* @property {boolean} isInitialized - Whether the first vault has been created.
|
||||||
* @property {boolean} isUnlocked - Whether the vault is currently decrypted and accounts are available for selection.
|
* @property {boolean} isUnlocked - Whether the vault is currently decrypted and accounts are available for selection.
|
||||||
* @property {boolean} isAccountMenuOpen - Represents whether the main account selection UI is currently displayed.
|
* @property {boolean} isAccountMenuOpen - Represents whether the main account selection UI is currently displayed.
|
||||||
|
* @property {boolean} isNetworkMenuOpen - Represents whether the main network selection UI is currently displayed.
|
||||||
* @property {object} identities - An object matching lower-case hex addresses to Identity objects with "address" and "name" (nickname) keys.
|
* @property {object} identities - An object matching lower-case hex addresses to Identity objects with "address" and "name" (nickname) keys.
|
||||||
* @property {object} unapprovedTxs - An object mapping transaction hashes to unapproved transactions.
|
* @property {object} unapprovedTxs - An object mapping transaction hashes to unapproved transactions.
|
||||||
* @property {object} networkConfigurations - A list of network configurations, containing RPC provider details (eg chainId, rpcUrl, rpcPreferences).
|
* @property {object} networkConfigurations - A list of network configurations, containing RPC provider details (eg chainId, rpcUrl, rpcPreferences).
|
||||||
@ -688,7 +688,7 @@ export function setupController(
|
|||||||
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
|
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
|
||||||
updateBadge,
|
updateBadge,
|
||||||
);
|
);
|
||||||
controller.encryptionPublicKeyManager.on(
|
controller.encryptionPublicKeyController.hub.on(
|
||||||
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
|
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
|
||||||
updateBadge,
|
updateBadge,
|
||||||
);
|
);
|
||||||
@ -727,20 +727,13 @@ export function setupController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getUnapprovedTransactionCount() {
|
function getUnapprovedTransactionCount() {
|
||||||
const unapprovedTxCount = controller.txController.getUnapprovedTxCount();
|
|
||||||
const { unapprovedDecryptMsgCount } = controller.decryptMessageManager;
|
const { unapprovedDecryptMsgCount } = controller.decryptMessageManager;
|
||||||
const { unapprovedEncryptionPublicKeyMsgCount } =
|
|
||||||
controller.encryptionPublicKeyManager;
|
|
||||||
const pendingApprovalCount =
|
const pendingApprovalCount =
|
||||||
controller.approvalController.getTotalApprovalCount();
|
controller.approvalController.getTotalApprovalCount();
|
||||||
const waitingForUnlockCount =
|
const waitingForUnlockCount =
|
||||||
controller.appStateController.waitingForUnlock.length;
|
controller.appStateController.waitingForUnlock.length;
|
||||||
return (
|
return (
|
||||||
unapprovedTxCount +
|
unapprovedDecryptMsgCount + pendingApprovalCount + waitingForUnlockCount
|
||||||
unapprovedDecryptMsgCount +
|
|
||||||
unapprovedEncryptionPublicKeyMsgCount +
|
|
||||||
pendingApprovalCount +
|
|
||||||
waitingForUnlockCount
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,13 +763,8 @@ export function setupController(
|
|||||||
REJECT_NOTIFICATION_CLOSE,
|
REJECT_NOTIFICATION_CLOSE,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
controller.encryptionPublicKeyManager.messages
|
controller.encryptionPublicKeyController.rejectUnapproved(
|
||||||
.filter((msg) => msg.status === 'unapproved')
|
|
||||||
.forEach((tx) =>
|
|
||||||
controller.encryptionPublicKeyManager.rejectMsg(
|
|
||||||
tx.id,
|
|
||||||
REJECT_NOTIFICATION_CLOSE,
|
REJECT_NOTIFICATION_CLOSE,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Finally, resolve snap dialog approvals on Flask and reject all the others managed by the ApprovalController.
|
// Finally, resolve snap dialog approvals on Flask and reject all the others managed by the ApprovalController.
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import { ObservableStore } from '@metamask/obs-store';
|
import { ObservableStore } from '@metamask/obs-store';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import log from 'loglevel';
|
||||||
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
|
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
|
||||||
import { MINUTE } from '../../../shared/constants/time';
|
import { MINUTE } from '../../../shared/constants/time';
|
||||||
import { AUTO_LOCK_TIMEOUT_ALARM } from '../../../shared/constants/alarms';
|
import { AUTO_LOCK_TIMEOUT_ALARM } from '../../../shared/constants/alarms';
|
||||||
@ -8,8 +10,11 @@ import { isBeta } from '../../../ui/helpers/utils/build-types';
|
|||||||
import {
|
import {
|
||||||
ENVIRONMENT_TYPE_BACKGROUND,
|
ENVIRONMENT_TYPE_BACKGROUND,
|
||||||
POLLING_TOKEN_ENVIRONMENT_TYPES,
|
POLLING_TOKEN_ENVIRONMENT_TYPES,
|
||||||
|
ORIGIN_METAMASK,
|
||||||
} from '../../../shared/constants/app';
|
} from '../../../shared/constants/app';
|
||||||
|
|
||||||
|
const APPROVAL_REQUEST_TYPE = 'unlock';
|
||||||
|
|
||||||
export default class AppStateController extends EventEmitter {
|
export default class AppStateController extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* @param {object} opts
|
* @param {object} opts
|
||||||
@ -20,9 +25,9 @@ export default class AppStateController extends EventEmitter {
|
|||||||
isUnlocked,
|
isUnlocked,
|
||||||
initState,
|
initState,
|
||||||
onInactiveTimeout,
|
onInactiveTimeout,
|
||||||
showUnlockRequest,
|
|
||||||
preferencesStore,
|
preferencesStore,
|
||||||
qrHardwareStore,
|
qrHardwareStore,
|
||||||
|
messenger,
|
||||||
} = opts;
|
} = opts;
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@ -59,8 +64,6 @@ export default class AppStateController extends EventEmitter {
|
|||||||
this.waitingForUnlock = [];
|
this.waitingForUnlock = [];
|
||||||
addUnlockListener(this.handleUnlock.bind(this));
|
addUnlockListener(this.handleUnlock.bind(this));
|
||||||
|
|
||||||
this._showUnlockRequest = showUnlockRequest;
|
|
||||||
|
|
||||||
preferencesStore.subscribe(({ preferences }) => {
|
preferencesStore.subscribe(({ preferences }) => {
|
||||||
const currentState = this.store.getState();
|
const currentState = this.store.getState();
|
||||||
if (currentState.timeoutMinutes !== preferences.autoLockTimeLimit) {
|
if (currentState.timeoutMinutes !== preferences.autoLockTimeLimit) {
|
||||||
@ -74,6 +77,9 @@ export default class AppStateController extends EventEmitter {
|
|||||||
|
|
||||||
const { preferences } = preferencesStore.getState();
|
const { preferences } = preferencesStore.getState();
|
||||||
this._setInactiveTimeout(preferences.autoLockTimeLimit);
|
this._setInactiveTimeout(preferences.autoLockTimeLimit);
|
||||||
|
|
||||||
|
this.messagingSystem = messenger;
|
||||||
|
this._approvalRequestId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,7 +114,7 @@ export default class AppStateController extends EventEmitter {
|
|||||||
this.waitingForUnlock.push({ resolve });
|
this.waitingForUnlock.push({ resolve });
|
||||||
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
|
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
|
||||||
if (shouldShowUnlockRequest) {
|
if (shouldShowUnlockRequest) {
|
||||||
this._showUnlockRequest();
|
this._requestApproval();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +128,8 @@ export default class AppStateController extends EventEmitter {
|
|||||||
}
|
}
|
||||||
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
|
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._acceptApproval();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,6 +152,37 @@ export default class AppStateController extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record that the user has been shown the recovery phrase reminder.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
setRecoveryPhraseReminderLastShown(lastShown) {
|
||||||
|
this.store.updateState({
|
||||||
|
recoveryPhraseReminderLastShown: lastShown,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record the timestamp of the last time the user has acceoted the terms of use
|
||||||
|
*
|
||||||
|
* @param {number} lastAgreed - timestamp when user last accepted the terms of use
|
||||||
|
*/
|
||||||
|
setTermsOfUseLastAgreed(lastAgreed) {
|
||||||
|
this.store.updateState({
|
||||||
|
termsOfUseLastAgreed: lastAgreed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record the timestamp of the last time the user has seen the outdated browser warning
|
* Record the timestamp of the last time the user has seen the outdated browser warning
|
||||||
*
|
*
|
||||||
@ -349,4 +388,39 @@ export default class AppStateController extends EventEmitter {
|
|||||||
serviceWorkerLastActiveTime,
|
serviceWorkerLastActiveTime,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_requestApproval() {
|
||||||
|
this._approvalRequestId = uuid();
|
||||||
|
|
||||||
|
this.messagingSystem
|
||||||
|
.call(
|
||||||
|
'ApprovalController:addRequest',
|
||||||
|
{
|
||||||
|
id: this._approvalRequestId,
|
||||||
|
origin: ORIGIN_METAMASK,
|
||||||
|
type: APPROVAL_REQUEST_TYPE,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.catch(() => {
|
||||||
|
// Intentionally ignored as promise not currently used
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_acceptApproval() {
|
||||||
|
if (!this._approvalRequestId) {
|
||||||
|
log.error('Attempted to accept missing unlock approval request');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.messagingSystem.call(
|
||||||
|
'ApprovalController:acceptRequest',
|
||||||
|
this._approvalRequestId,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error('Failed to accept transaction approval request', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._approvalRequestId = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,19 @@
|
|||||||
|
import { ObservableStore } from '@metamask/obs-store';
|
||||||
|
import log from 'loglevel';
|
||||||
|
import { ORIGIN_METAMASK } from '../../../shared/constants/app';
|
||||||
import AppStateController from './app-state';
|
import AppStateController from './app-state';
|
||||||
|
|
||||||
|
jest.mock('loglevel');
|
||||||
|
|
||||||
|
let appStateController, mockStore;
|
||||||
|
|
||||||
describe('AppStateController', () => {
|
describe('AppStateController', () => {
|
||||||
describe('setOutdatedBrowserWarningLastShown', () => {
|
mockStore = new ObservableStore();
|
||||||
it('should set the last shown time', () => {
|
const createAppStateController = (initState = {}) => {
|
||||||
const appStateController = new AppStateController({
|
return new AppStateController({
|
||||||
addUnlockListener: jest.fn(),
|
addUnlockListener: jest.fn(),
|
||||||
isUnlocked: jest.fn(() => true),
|
isUnlocked: jest.fn(() => true),
|
||||||
initState: {},
|
initState,
|
||||||
onInactiveTimeout: jest.fn(),
|
onInactiveTimeout: jest.fn(),
|
||||||
showUnlockRequest: jest.fn(),
|
showUnlockRequest: jest.fn(),
|
||||||
preferencesStore: {
|
preferencesStore: {
|
||||||
@ -20,7 +27,21 @@ describe('AppStateController', () => {
|
|||||||
qrHardwareStore: {
|
qrHardwareStore: {
|
||||||
subscribe: jest.fn(),
|
subscribe: jest.fn(),
|
||||||
},
|
},
|
||||||
|
messenger: {
|
||||||
|
call: jest.fn(() => ({
|
||||||
|
catch: jest.fn(),
|
||||||
|
})),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
appStateController = createAppStateController({ store: mockStore });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setOutdatedBrowserWarningLastShown', () => {
|
||||||
|
it('sets the last shown time', () => {
|
||||||
|
appStateController = createAppStateController();
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
|
|
||||||
appStateController.setOutdatedBrowserWarningLastShown(date);
|
appStateController.setOutdatedBrowserWarningLastShown(date);
|
||||||
@ -29,5 +50,300 @@ describe('AppStateController', () => {
|
|||||||
appStateController.store.getState().outdatedBrowserWarningLastShown,
|
appStateController.store.getState().outdatedBrowserWarningLastShown,
|
||||||
).toStrictEqual(date);
|
).toStrictEqual(date);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets outdated browser warning last shown timestamp', () => {
|
||||||
|
const lastShownTimestamp = Date.now();
|
||||||
|
appStateController = createAppStateController();
|
||||||
|
const updateStateSpy = jest.spyOn(
|
||||||
|
appStateController.store,
|
||||||
|
'updateState',
|
||||||
|
);
|
||||||
|
|
||||||
|
appStateController.setOutdatedBrowserWarningLastShown(lastShownTimestamp);
|
||||||
|
|
||||||
|
expect(updateStateSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(updateStateSpy).toHaveBeenCalledWith({
|
||||||
|
outdatedBrowserWarningLastShown: lastShownTimestamp,
|
||||||
|
});
|
||||||
|
|
||||||
|
updateStateSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getUnlockPromise', () => {
|
||||||
|
it('waits for unlock if the extension is locked', async () => {
|
||||||
|
appStateController = createAppStateController();
|
||||||
|
const isUnlockedMock = jest
|
||||||
|
.spyOn(appStateController, 'isUnlocked')
|
||||||
|
.mockReturnValue(false);
|
||||||
|
const waitForUnlockSpy = jest.spyOn(appStateController, 'waitForUnlock');
|
||||||
|
|
||||||
|
appStateController.getUnlockPromise(true);
|
||||||
|
expect(isUnlockedMock).toHaveBeenCalled();
|
||||||
|
expect(waitForUnlockSpy).toHaveBeenCalledWith(expect.any(Function), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resolves immediately if the extension is already unlocked', async () => {
|
||||||
|
appStateController = createAppStateController();
|
||||||
|
const isUnlockedMock = jest
|
||||||
|
.spyOn(appStateController, 'isUnlocked')
|
||||||
|
.mockReturnValue(true);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
appStateController.getUnlockPromise(false),
|
||||||
|
).resolves.toBeUndefined();
|
||||||
|
|
||||||
|
expect(isUnlockedMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('waitForUnlock', () => {
|
||||||
|
it('resolves immediately if already unlocked', async () => {
|
||||||
|
const emitSpy = jest.spyOn(appStateController, 'emit');
|
||||||
|
const resolveFn = jest.fn();
|
||||||
|
appStateController.waitForUnlock(resolveFn, false);
|
||||||
|
expect(emitSpy).toHaveBeenCalledWith('updateBadge');
|
||||||
|
expect(appStateController.messagingSystem.call).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates approval request when waitForUnlock is called with shouldShowUnlockRequest as true', async () => {
|
||||||
|
jest.spyOn(appStateController, 'isUnlocked').mockReturnValue(false);
|
||||||
|
|
||||||
|
const resolveFn = jest.fn();
|
||||||
|
appStateController.waitForUnlock(resolveFn, true);
|
||||||
|
|
||||||
|
expect(appStateController.messagingSystem.call).toHaveBeenCalledTimes(1);
|
||||||
|
expect(appStateController.messagingSystem.call).toHaveBeenCalledWith(
|
||||||
|
'ApprovalController:addRequest',
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
origin: ORIGIN_METAMASK,
|
||||||
|
type: 'unlock',
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('handleUnlock', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.spyOn(appStateController, 'isUnlocked').mockReturnValue(false);
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
it('accepts approval request revolving all the related promises', async () => {
|
||||||
|
const emitSpy = jest.spyOn(appStateController, 'emit');
|
||||||
|
const resolveFn = jest.fn();
|
||||||
|
appStateController.waitForUnlock(resolveFn, true);
|
||||||
|
|
||||||
|
appStateController.handleUnlock();
|
||||||
|
|
||||||
|
expect(emitSpy).toHaveBeenCalled();
|
||||||
|
expect(emitSpy).toHaveBeenCalledWith('updateBadge');
|
||||||
|
expect(appStateController.messagingSystem.call).toHaveBeenCalled();
|
||||||
|
expect(appStateController.messagingSystem.call).toHaveBeenCalledWith(
|
||||||
|
'ApprovalController:acceptRequest',
|
||||||
|
expect.any(String),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs if rejecting approval request throws', async () => {
|
||||||
|
appStateController._approvalRequestId = 'mock-approval-request-id';
|
||||||
|
appStateController = new AppStateController({
|
||||||
|
addUnlockListener: jest.fn(),
|
||||||
|
isUnlocked: jest.fn(() => true),
|
||||||
|
onInactiveTimeout: jest.fn(),
|
||||||
|
showUnlockRequest: jest.fn(),
|
||||||
|
preferencesStore: {
|
||||||
|
subscribe: jest.fn(),
|
||||||
|
getState: jest.fn(() => ({
|
||||||
|
preferences: {
|
||||||
|
autoLockTimeLimit: 0,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
qrHardwareStore: {
|
||||||
|
subscribe: jest.fn(),
|
||||||
|
},
|
||||||
|
messenger: {
|
||||||
|
call: jest.fn(() => {
|
||||||
|
throw new Error('mock error');
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
appStateController.handleUnlock();
|
||||||
|
|
||||||
|
expect(log.error).toHaveBeenCalledTimes(1);
|
||||||
|
expect(log.error).toHaveBeenCalledWith(
|
||||||
|
'Attempted to accept missing unlock approval request',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns without call messenger if no approval request in pending', async () => {
|
||||||
|
const emitSpy = jest.spyOn(appStateController, 'emit');
|
||||||
|
|
||||||
|
appStateController.handleUnlock();
|
||||||
|
|
||||||
|
expect(emitSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(appStateController.messagingSystem.call).toHaveBeenCalledTimes(0);
|
||||||
|
expect(log.error).toHaveBeenCalledTimes(1);
|
||||||
|
expect(log.error).toHaveBeenCalledWith(
|
||||||
|
'Attempted to accept missing unlock approval request',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setDefaultHomeActiveTabName', () => {
|
||||||
|
it('sets the default home tab name', () => {
|
||||||
|
appStateController.setDefaultHomeActiveTabName('testTabName');
|
||||||
|
expect(appStateController.store.getState().defaultHomeActiveTabName).toBe(
|
||||||
|
'testTabName',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setConnectedStatusPopoverHasBeenShown', () => {
|
||||||
|
it('sets connected status popover as shown', () => {
|
||||||
|
appStateController.setConnectedStatusPopoverHasBeenShown();
|
||||||
|
expect(
|
||||||
|
appStateController.store.getState().connectedStatusPopoverHasBeenShown,
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setRecoveryPhraseReminderHasBeenShown', () => {
|
||||||
|
it('sets recovery phrase reminder as shown', () => {
|
||||||
|
appStateController.setRecoveryPhraseReminderHasBeenShown();
|
||||||
|
expect(
|
||||||
|
appStateController.store.getState().recoveryPhraseReminderHasBeenShown,
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setRecoveryPhraseReminderLastShown', () => {
|
||||||
|
it('sets the last shown time of recovery phrase reminder', () => {
|
||||||
|
const timestamp = Date.now();
|
||||||
|
appStateController.setRecoveryPhraseReminderLastShown(timestamp);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
appStateController.store.getState().recoveryPhraseReminderLastShown,
|
||||||
|
).toBe(timestamp);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setLastActiveTime', () => {
|
||||||
|
it('sets the last active time to the current time', () => {
|
||||||
|
const spy = jest.spyOn(appStateController, '_resetTimer');
|
||||||
|
appStateController.setLastActiveTime();
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setBrowserEnvironment', () => {
|
||||||
|
it('sets the current browser and OS environment', () => {
|
||||||
|
appStateController.setBrowserEnvironment('Windows', 'Chrome');
|
||||||
|
expect(
|
||||||
|
appStateController.store.getState().browserEnvironment,
|
||||||
|
).toStrictEqual({
|
||||||
|
os: 'Windows',
|
||||||
|
browser: 'Chrome',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('addPollingToken', () => {
|
||||||
|
it('adds a pollingToken for a given environmentType', () => {
|
||||||
|
const pollingTokenType = 'popupGasPollTokens';
|
||||||
|
appStateController.addPollingToken('token1', pollingTokenType);
|
||||||
|
expect(appStateController.store.getState()[pollingTokenType]).toContain(
|
||||||
|
'token1',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removePollingToken', () => {
|
||||||
|
it('removes a pollingToken for a given environmentType', () => {
|
||||||
|
const pollingTokenType = 'popupGasPollTokens';
|
||||||
|
appStateController.addPollingToken('token1', pollingTokenType);
|
||||||
|
appStateController.removePollingToken('token1', pollingTokenType);
|
||||||
|
expect(
|
||||||
|
appStateController.store.getState()[pollingTokenType],
|
||||||
|
).not.toContain('token1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('clearPollingTokens', () => {
|
||||||
|
it('clears all pollingTokens', () => {
|
||||||
|
appStateController.addPollingToken('token1', 'popupGasPollTokens');
|
||||||
|
appStateController.addPollingToken('token2', 'notificationGasPollTokens');
|
||||||
|
appStateController.addPollingToken('token3', 'fullScreenGasPollTokens');
|
||||||
|
appStateController.clearPollingTokens();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
appStateController.store.getState().popupGasPollTokens,
|
||||||
|
).toStrictEqual([]);
|
||||||
|
expect(
|
||||||
|
appStateController.store.getState().notificationGasPollTokens,
|
||||||
|
).toStrictEqual([]);
|
||||||
|
expect(
|
||||||
|
appStateController.store.getState().fullScreenGasPollTokens,
|
||||||
|
).toStrictEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setShowTestnetMessageInDropdown', () => {
|
||||||
|
it('sets whether the testnet dismissal link should be shown in the network dropdown', () => {
|
||||||
|
appStateController.setShowTestnetMessageInDropdown(true);
|
||||||
|
expect(
|
||||||
|
appStateController.store.getState().showTestnetMessageInDropdown,
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
appStateController.setShowTestnetMessageInDropdown(false);
|
||||||
|
expect(
|
||||||
|
appStateController.store.getState().showTestnetMessageInDropdown,
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setShowBetaHeader', () => {
|
||||||
|
it('sets whether the beta notification heading on the home page', () => {
|
||||||
|
appStateController.setShowBetaHeader(true);
|
||||||
|
expect(appStateController.store.getState().showBetaHeader).toBe(true);
|
||||||
|
|
||||||
|
appStateController.setShowBetaHeader(false);
|
||||||
|
expect(appStateController.store.getState().showBetaHeader).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setCurrentPopupId', () => {
|
||||||
|
it('sets the currentPopupId in the appState', () => {
|
||||||
|
const popupId = 'popup1';
|
||||||
|
|
||||||
|
appStateController.setCurrentPopupId(popupId);
|
||||||
|
expect(appStateController.store.getState().currentPopupId).toBe(popupId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getCurrentPopupId', () => {
|
||||||
|
it('retrieves the currentPopupId saved in the appState', () => {
|
||||||
|
const popupId = 'popup1';
|
||||||
|
|
||||||
|
appStateController.setCurrentPopupId(popupId);
|
||||||
|
expect(appStateController.getCurrentPopupId()).toBe(popupId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setFirstTimeUsedNetwork', () => {
|
||||||
|
it('updates the array of the first time used networks', () => {
|
||||||
|
const chainId = '0x1';
|
||||||
|
|
||||||
|
appStateController.setFirstTimeUsedNetwork(chainId);
|
||||||
|
expect(appStateController.store.getState().usedNetworks[chainId]).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -13,7 +13,7 @@ import { convertHexToDecimal } from '@metamask/controller-utils';
|
|||||||
import { NETWORK_TYPES } from '../../../shared/constants/network';
|
import { NETWORK_TYPES } from '../../../shared/constants/network';
|
||||||
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
|
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
|
||||||
import DetectTokensController from './detect-tokens';
|
import DetectTokensController from './detect-tokens';
|
||||||
import NetworkController, { NetworkControllerEventTypes } from './network';
|
import { NetworkController, NetworkControllerEventType } from './network';
|
||||||
import PreferencesController from './preferences';
|
import PreferencesController from './preferences';
|
||||||
|
|
||||||
describe('DetectTokensController', function () {
|
describe('DetectTokensController', function () {
|
||||||
@ -248,7 +248,7 @@ describe('DetectTokensController', function () {
|
|||||||
),
|
),
|
||||||
onNetworkStateChange: (cb) =>
|
onNetworkStateChange: (cb) =>
|
||||||
networkControllerMessenger.subscribe(
|
networkControllerMessenger.subscribe(
|
||||||
NetworkControllerEventTypes.NetworkDidChange,
|
NetworkControllerEventType.NetworkDidChange,
|
||||||
() => {
|
() => {
|
||||||
const networkState = network.store.getState();
|
const networkState = network.store.getState();
|
||||||
const modifiedNetworkState = {
|
const modifiedNetworkState = {
|
||||||
|
400
app/scripts/controllers/encryption-public-key.test.ts
Normal file
400
app/scripts/controllers/encryption-public-key.test.ts
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
import { EncryptionPublicKeyManager } from '@metamask/message-manager';
|
||||||
|
import {
|
||||||
|
AbstractMessage,
|
||||||
|
OriginalRequest,
|
||||||
|
} from '@metamask/message-manager/dist/AbstractMessageManager';
|
||||||
|
import { KeyringType } from '../../../shared/constants/keyring';
|
||||||
|
import { MetaMetricsEventCategory } from '../../../shared/constants/metametrics';
|
||||||
|
import EncryptionPublicKeyController, {
|
||||||
|
EncryptionPublicKeyControllerMessenger,
|
||||||
|
EncryptionPublicKeyControllerOptions,
|
||||||
|
} from './encryption-public-key';
|
||||||
|
|
||||||
|
jest.mock('@metamask/message-manager', () => ({
|
||||||
|
EncryptionPublicKeyManager: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const messageIdMock = '123';
|
||||||
|
const messageIdMock2 = '456';
|
||||||
|
const stateMock = { test: 123 };
|
||||||
|
const addressMock = '0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d';
|
||||||
|
const publicKeyMock = '32762347862378feb87123781623a=';
|
||||||
|
const keyringMock = { type: KeyringType.hdKeyTree };
|
||||||
|
|
||||||
|
const messageParamsMock = {
|
||||||
|
from: addressMock,
|
||||||
|
origin: 'http://test.com',
|
||||||
|
data: addressMock,
|
||||||
|
metamaskId: messageIdMock,
|
||||||
|
};
|
||||||
|
|
||||||
|
const messageMock = {
|
||||||
|
id: messageIdMock,
|
||||||
|
time: 123,
|
||||||
|
status: 'unapproved',
|
||||||
|
type: 'testType',
|
||||||
|
rawSig: undefined,
|
||||||
|
} as any as AbstractMessage;
|
||||||
|
|
||||||
|
const coreMessageMock = {
|
||||||
|
...messageMock,
|
||||||
|
messageParams: messageParamsMock,
|
||||||
|
};
|
||||||
|
|
||||||
|
const stateMessageMock = {
|
||||||
|
...messageMock,
|
||||||
|
msgParams: addressMock,
|
||||||
|
origin: messageParamsMock.origin,
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestMock = {
|
||||||
|
origin: 'http://test2.com',
|
||||||
|
} as OriginalRequest;
|
||||||
|
|
||||||
|
const createMessengerMock = () =>
|
||||||
|
({
|
||||||
|
registerActionHandler: jest.fn(),
|
||||||
|
publish: jest.fn(),
|
||||||
|
call: jest.fn(),
|
||||||
|
} as any as jest.Mocked<EncryptionPublicKeyControllerMessenger>);
|
||||||
|
|
||||||
|
const createEncryptionPublicKeyManagerMock = <T>() =>
|
||||||
|
({
|
||||||
|
getUnapprovedMessages: jest.fn(),
|
||||||
|
getUnapprovedMessagesCount: jest.fn(),
|
||||||
|
addUnapprovedMessageAsync: jest.fn(),
|
||||||
|
approveMessage: jest.fn(),
|
||||||
|
setMessageStatusAndResult: jest.fn(),
|
||||||
|
rejectMessage: jest.fn(),
|
||||||
|
subscribe: jest.fn(),
|
||||||
|
update: jest.fn(),
|
||||||
|
hub: {
|
||||||
|
on: jest.fn(),
|
||||||
|
},
|
||||||
|
} as any as jest.Mocked<T>);
|
||||||
|
|
||||||
|
const createKeyringControllerMock = () => ({
|
||||||
|
getKeyringForAccount: jest.fn(),
|
||||||
|
getEncryptionPublicKey: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('EncryptionPublicKeyController', () => {
|
||||||
|
let encryptionPublicKeyController: EncryptionPublicKeyController;
|
||||||
|
|
||||||
|
const encryptionPublicKeyManagerConstructorMock =
|
||||||
|
EncryptionPublicKeyManager as jest.MockedClass<
|
||||||
|
typeof EncryptionPublicKeyManager
|
||||||
|
>;
|
||||||
|
const encryptionPublicKeyManagerMock =
|
||||||
|
createEncryptionPublicKeyManagerMock<EncryptionPublicKeyManager>();
|
||||||
|
const messengerMock = createMessengerMock();
|
||||||
|
const keyringControllerMock = createKeyringControllerMock();
|
||||||
|
const getStateMock = jest.fn();
|
||||||
|
const metricsEventMock = jest.fn();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
|
||||||
|
encryptionPublicKeyManagerConstructorMock.mockReturnValue(
|
||||||
|
encryptionPublicKeyManagerMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
encryptionPublicKeyController = new EncryptionPublicKeyController({
|
||||||
|
messenger: messengerMock as any,
|
||||||
|
keyringController: keyringControllerMock as any,
|
||||||
|
getState: getStateMock as any,
|
||||||
|
metricsEvent: metricsEventMock as any,
|
||||||
|
} as EncryptionPublicKeyControllerOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('unapprovedMsgCount', () => {
|
||||||
|
it('returns value from message manager getter', () => {
|
||||||
|
encryptionPublicKeyManagerMock.getUnapprovedMessagesCount.mockReturnValueOnce(
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
expect(encryptionPublicKeyController.unapprovedMsgCount).toBe(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('resetState', () => {
|
||||||
|
it('sets state to initial state', () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
encryptionPublicKeyController.update(() => ({
|
||||||
|
unapprovedEncryptionPublicKeyMsgs: {
|
||||||
|
[messageIdMock]: messageMock,
|
||||||
|
} as any,
|
||||||
|
unapprovedEncryptionPublicKeyMsgCount: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
encryptionPublicKeyController.resetState();
|
||||||
|
|
||||||
|
expect(encryptionPublicKeyController.state).toEqual({
|
||||||
|
unapprovedEncryptionPublicKeyMsgs: {},
|
||||||
|
unapprovedEncryptionPublicKeyMsgCount: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('rejectUnapproved', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const messages = {
|
||||||
|
[messageIdMock]: messageMock,
|
||||||
|
[messageIdMock2]: messageMock,
|
||||||
|
};
|
||||||
|
encryptionPublicKeyManagerMock.getUnapprovedMessages.mockReturnValueOnce(
|
||||||
|
messages as any,
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
encryptionPublicKeyController.update(() => ({
|
||||||
|
unapprovedEncryptionPublicKeyMsgs: messages as any,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects all messages in the message manager', () => {
|
||||||
|
encryptionPublicKeyController.rejectUnapproved('Test Reason');
|
||||||
|
expect(
|
||||||
|
encryptionPublicKeyManagerMock.rejectMessage,
|
||||||
|
).toHaveBeenCalledTimes(2);
|
||||||
|
expect(encryptionPublicKeyManagerMock.rejectMessage).toHaveBeenCalledWith(
|
||||||
|
messageIdMock,
|
||||||
|
);
|
||||||
|
expect(encryptionPublicKeyManagerMock.rejectMessage).toHaveBeenCalledWith(
|
||||||
|
messageIdMock2,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fires metrics event with reject reason', () => {
|
||||||
|
encryptionPublicKeyController.rejectUnapproved('Test Reason');
|
||||||
|
expect(metricsEventMock).toHaveBeenCalledTimes(2);
|
||||||
|
expect(metricsEventMock).toHaveBeenLastCalledWith({
|
||||||
|
event: 'Test Reason',
|
||||||
|
category: MetaMetricsEventCategory.Messages,
|
||||||
|
properties: {
|
||||||
|
action: 'Encryption public key Request',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('clearUnapproved', () => {
|
||||||
|
it('resets state in all message managers', () => {
|
||||||
|
encryptionPublicKeyController.clearUnapproved();
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
unapprovedMessages: {},
|
||||||
|
unapprovedMessagesCount: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(encryptionPublicKeyManagerMock.update).toHaveBeenCalledTimes(1);
|
||||||
|
expect(encryptionPublicKeyManagerMock.update).toHaveBeenCalledWith(
|
||||||
|
defaultState,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('newRequestEncryptionPublicKey', () => {
|
||||||
|
it.each([
|
||||||
|
['Ledger', KeyringType.ledger],
|
||||||
|
['Trezor', KeyringType.trezor],
|
||||||
|
['Lattice', KeyringType.lattice],
|
||||||
|
['QR hardware', KeyringType.qr],
|
||||||
|
])(
|
||||||
|
'throws if keyring is not supported',
|
||||||
|
async (keyringName, keyringType) => {
|
||||||
|
keyringControllerMock.getKeyringForAccount.mockResolvedValueOnce({
|
||||||
|
type: keyringType,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
encryptionPublicKeyController.newRequestEncryptionPublicKey(
|
||||||
|
addressMock,
|
||||||
|
requestMock,
|
||||||
|
),
|
||||||
|
).rejects.toThrowError(
|
||||||
|
`${keyringName} does not support eth_getEncryptionPublicKey.`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it('adds message to message manager', async () => {
|
||||||
|
keyringControllerMock.getKeyringForAccount.mockResolvedValueOnce(
|
||||||
|
keyringMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
await encryptionPublicKeyController.newRequestEncryptionPublicKey(
|
||||||
|
addressMock,
|
||||||
|
requestMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
encryptionPublicKeyManagerMock.addUnapprovedMessageAsync,
|
||||||
|
).toHaveBeenCalledTimes(1);
|
||||||
|
expect(
|
||||||
|
encryptionPublicKeyManagerMock.addUnapprovedMessageAsync,
|
||||||
|
).toHaveBeenCalledWith({ from: addressMock }, requestMock);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('encryptionPublicKey', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
encryptionPublicKeyManagerMock.approveMessage.mockResolvedValueOnce({
|
||||||
|
from: messageParamsMock.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
keyringControllerMock.getEncryptionPublicKey.mockResolvedValueOnce(
|
||||||
|
publicKeyMock,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('approves message and signs', async () => {
|
||||||
|
await encryptionPublicKeyController.encryptionPublicKey(
|
||||||
|
messageParamsMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
keyringControllerMock.getEncryptionPublicKey,
|
||||||
|
).toHaveBeenCalledTimes(1);
|
||||||
|
expect(keyringControllerMock.getEncryptionPublicKey).toHaveBeenCalledWith(
|
||||||
|
messageParamsMock.data,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
encryptionPublicKeyManagerMock.setMessageStatusAndResult,
|
||||||
|
).toHaveBeenCalledTimes(1);
|
||||||
|
expect(
|
||||||
|
encryptionPublicKeyManagerMock.setMessageStatusAndResult,
|
||||||
|
).toHaveBeenCalledWith(
|
||||||
|
messageParamsMock.metamaskId,
|
||||||
|
publicKeyMock,
|
||||||
|
'received',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns current state', async () => {
|
||||||
|
getStateMock.mockReturnValueOnce(stateMock);
|
||||||
|
expect(
|
||||||
|
await encryptionPublicKeyController.encryptionPublicKey(
|
||||||
|
messageParamsMock,
|
||||||
|
),
|
||||||
|
).toEqual(stateMock);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts approval', async () => {
|
||||||
|
await encryptionPublicKeyController.encryptionPublicKey(
|
||||||
|
messageParamsMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(messengerMock.call).toHaveBeenCalledTimes(1);
|
||||||
|
expect(messengerMock.call).toHaveBeenCalledWith(
|
||||||
|
'ApprovalController:acceptRequest',
|
||||||
|
messageParamsMock.metamaskId,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects message on error', async () => {
|
||||||
|
keyringControllerMock.getEncryptionPublicKey.mockReset();
|
||||||
|
keyringControllerMock.getEncryptionPublicKey.mockRejectedValue(
|
||||||
|
new Error('Test Error'),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
encryptionPublicKeyController.encryptionPublicKey(messageParamsMock),
|
||||||
|
).rejects.toThrow('Test Error');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
encryptionPublicKeyManagerMock.rejectMessage,
|
||||||
|
).toHaveBeenCalledTimes(1);
|
||||||
|
expect(encryptionPublicKeyManagerMock.rejectMessage).toHaveBeenCalledWith(
|
||||||
|
messageParamsMock.metamaskId,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects approval on error', async () => {
|
||||||
|
keyringControllerMock.getEncryptionPublicKey.mockReset();
|
||||||
|
keyringControllerMock.getEncryptionPublicKey.mockRejectedValue(
|
||||||
|
new Error('Test Error'),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
encryptionPublicKeyController.encryptionPublicKey(messageParamsMock),
|
||||||
|
).rejects.toThrow('Test Error');
|
||||||
|
|
||||||
|
expect(messengerMock.call).toHaveBeenCalledTimes(1);
|
||||||
|
expect(messengerMock.call).toHaveBeenCalledWith(
|
||||||
|
'ApprovalController:rejectRequest',
|
||||||
|
messageParamsMock.metamaskId,
|
||||||
|
'Cancel',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cancelEncryptionPublicKey', () => {
|
||||||
|
it('rejects message using message manager', async () => {
|
||||||
|
encryptionPublicKeyController.cancelEncryptionPublicKey(messageIdMock);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
encryptionPublicKeyManagerMock.rejectMessage,
|
||||||
|
).toHaveBeenCalledTimes(1);
|
||||||
|
expect(encryptionPublicKeyManagerMock.rejectMessage).toHaveBeenCalledWith(
|
||||||
|
messageParamsMock.metamaskId,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects approval using approval controller', async () => {
|
||||||
|
encryptionPublicKeyController.cancelEncryptionPublicKey(messageIdMock);
|
||||||
|
|
||||||
|
expect(messengerMock.call).toHaveBeenCalledTimes(1);
|
||||||
|
expect(messengerMock.call).toHaveBeenCalledWith(
|
||||||
|
'ApprovalController:rejectRequest',
|
||||||
|
messageParamsMock.metamaskId,
|
||||||
|
'Cancel',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('message manager events', () => {
|
||||||
|
it('bubbles update badge event from EncryptionPublicKeyManager', () => {
|
||||||
|
const mockListener = jest.fn();
|
||||||
|
|
||||||
|
encryptionPublicKeyController.hub.on('updateBadge', mockListener);
|
||||||
|
(encryptionPublicKeyManagerMock.hub.on as any).mock.calls[0][1]();
|
||||||
|
|
||||||
|
expect(mockListener).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('requires approval on unapproved message event from EncryptionPublicKeyManager', () => {
|
||||||
|
messengerMock.call.mockResolvedValueOnce({});
|
||||||
|
|
||||||
|
(encryptionPublicKeyManagerMock.hub.on as any).mock.calls[1][1](
|
||||||
|
messageParamsMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(messengerMock.call).toHaveBeenCalledTimes(1);
|
||||||
|
expect(messengerMock.call).toHaveBeenCalledWith(
|
||||||
|
'ApprovalController:addRequest',
|
||||||
|
{
|
||||||
|
id: messageIdMock,
|
||||||
|
origin: messageParamsMock.origin,
|
||||||
|
type: 'eth_getEncryptionPublicKey',
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates state on EncryptionPublicKeyManager state change', async () => {
|
||||||
|
await encryptionPublicKeyManagerMock.subscribe.mock.calls[0][0]({
|
||||||
|
unapprovedMessages: { [messageIdMock]: coreMessageMock as any },
|
||||||
|
unapprovedMessagesCount: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(encryptionPublicKeyController.state).toEqual({
|
||||||
|
unapprovedEncryptionPublicKeyMsgs: {
|
||||||
|
[messageIdMock]: stateMessageMock as any,
|
||||||
|
},
|
||||||
|
unapprovedEncryptionPublicKeyMsgCount: 3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
421
app/scripts/controllers/encryption-public-key.ts
Normal file
421
app/scripts/controllers/encryption-public-key.ts
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
import EventEmitter from 'events';
|
||||||
|
import log from 'loglevel';
|
||||||
|
import {
|
||||||
|
EncryptionPublicKeyManager,
|
||||||
|
EncryptionPublicKeyParamsMetamask,
|
||||||
|
} from '@metamask/message-manager';
|
||||||
|
import { KeyringController } from '@metamask/eth-keyring-controller';
|
||||||
|
import {
|
||||||
|
AbstractMessageManager,
|
||||||
|
AbstractMessage,
|
||||||
|
MessageManagerState,
|
||||||
|
AbstractMessageParams,
|
||||||
|
AbstractMessageParamsMetamask,
|
||||||
|
OriginalRequest,
|
||||||
|
} from '@metamask/message-manager/dist/AbstractMessageManager';
|
||||||
|
import {
|
||||||
|
BaseControllerV2,
|
||||||
|
RestrictedControllerMessenger,
|
||||||
|
} from '@metamask/base-controller';
|
||||||
|
import { Patch } from 'immer';
|
||||||
|
import {
|
||||||
|
AcceptRequest,
|
||||||
|
AddApprovalRequest,
|
||||||
|
RejectRequest,
|
||||||
|
} from '@metamask/approval-controller';
|
||||||
|
import { MetaMetricsEventCategory } from '../../../shared/constants/metametrics';
|
||||||
|
import { KeyringType } from '../../../shared/constants/keyring';
|
||||||
|
import { ORIGIN_METAMASK } from '../../../shared/constants/app';
|
||||||
|
|
||||||
|
const controllerName = 'EncryptionPublicKeyController';
|
||||||
|
const methodNameGetEncryptionPublicKey = 'eth_getEncryptionPublicKey';
|
||||||
|
|
||||||
|
const stateMetadata = {
|
||||||
|
unapprovedEncryptionPublicKeyMsgs: { persist: false, anonymous: false },
|
||||||
|
unapprovedEncryptionPublicKeyMsgCount: { persist: false, anonymous: false },
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDefaultState = () => ({
|
||||||
|
unapprovedEncryptionPublicKeyMsgs: {},
|
||||||
|
unapprovedEncryptionPublicKeyMsgCount: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CoreMessage = AbstractMessage & {
|
||||||
|
messageParams: AbstractMessageParams;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StateMessage = Required<
|
||||||
|
Omit<AbstractMessage, 'securityProviderResponse'>
|
||||||
|
> & {
|
||||||
|
msgParams: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EncryptionPublicKeyControllerState = {
|
||||||
|
unapprovedEncryptionPublicKeyMsgs: Record<string, StateMessage>;
|
||||||
|
unapprovedEncryptionPublicKeyMsgCount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetEncryptionPublicKeyState = {
|
||||||
|
type: `${typeof controllerName}:getState`;
|
||||||
|
handler: () => EncryptionPublicKeyControllerState;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EncryptionPublicKeyStateChange = {
|
||||||
|
type: `${typeof controllerName}:stateChange`;
|
||||||
|
payload: [EncryptionPublicKeyControllerState, Patch[]];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EncryptionPublicKeyControllerActions = GetEncryptionPublicKeyState;
|
||||||
|
|
||||||
|
export type EncryptionPublicKeyControllerEvents =
|
||||||
|
EncryptionPublicKeyStateChange;
|
||||||
|
|
||||||
|
type AllowedActions = AddApprovalRequest | AcceptRequest | RejectRequest;
|
||||||
|
|
||||||
|
export type EncryptionPublicKeyControllerMessenger =
|
||||||
|
RestrictedControllerMessenger<
|
||||||
|
typeof controllerName,
|
||||||
|
EncryptionPublicKeyControllerActions | AllowedActions,
|
||||||
|
EncryptionPublicKeyControllerEvents,
|
||||||
|
AllowedActions['type'],
|
||||||
|
never
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type EncryptionPublicKeyControllerOptions = {
|
||||||
|
messenger: EncryptionPublicKeyControllerMessenger;
|
||||||
|
keyringController: KeyringController;
|
||||||
|
getState: () => any;
|
||||||
|
metricsEvent: (payload: any, options?: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for requesting encryption public key requests requiring user approval.
|
||||||
|
*/
|
||||||
|
export default class EncryptionPublicKeyController extends BaseControllerV2<
|
||||||
|
typeof controllerName,
|
||||||
|
EncryptionPublicKeyControllerState,
|
||||||
|
EncryptionPublicKeyControllerMessenger
|
||||||
|
> {
|
||||||
|
hub: EventEmitter;
|
||||||
|
|
||||||
|
private _keyringController: KeyringController;
|
||||||
|
|
||||||
|
private _getState: () => any;
|
||||||
|
|
||||||
|
private _encryptionPublicKeyManager: EncryptionPublicKeyManager;
|
||||||
|
|
||||||
|
private _metricsEvent: (payload: any, options?: any) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a EncryptionPublicKey controller.
|
||||||
|
*
|
||||||
|
* @param options - The controller options.
|
||||||
|
* @param options.messenger - The restricted controller messenger for the EncryptionPublicKey controller.
|
||||||
|
* @param options.keyringController - An instance of a keyring controller used to extract the encryption public key.
|
||||||
|
* @param options.getState - Callback to retrieve all user state.
|
||||||
|
* @param options.metricsEvent - A function for emitting a metric event.
|
||||||
|
*/
|
||||||
|
constructor({
|
||||||
|
messenger,
|
||||||
|
keyringController,
|
||||||
|
getState,
|
||||||
|
metricsEvent,
|
||||||
|
}: EncryptionPublicKeyControllerOptions) {
|
||||||
|
super({
|
||||||
|
name: controllerName,
|
||||||
|
metadata: stateMetadata,
|
||||||
|
messenger,
|
||||||
|
state: getDefaultState(),
|
||||||
|
});
|
||||||
|
|
||||||
|
this._keyringController = keyringController;
|
||||||
|
this._getState = getState;
|
||||||
|
this._metricsEvent = metricsEvent;
|
||||||
|
|
||||||
|
this.hub = new EventEmitter();
|
||||||
|
this._encryptionPublicKeyManager = new EncryptionPublicKeyManager(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
['received'],
|
||||||
|
);
|
||||||
|
|
||||||
|
this._encryptionPublicKeyManager.hub.on('updateBadge', () => {
|
||||||
|
this.hub.emit('updateBadge');
|
||||||
|
});
|
||||||
|
|
||||||
|
this._encryptionPublicKeyManager.hub.on(
|
||||||
|
'unapprovedMessage',
|
||||||
|
(msgParams: AbstractMessageParamsMetamask) => {
|
||||||
|
this._requestApproval(msgParams, methodNameGetEncryptionPublicKey);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
this._subscribeToMessageState(
|
||||||
|
this._encryptionPublicKeyManager,
|
||||||
|
(state, newMessages, messageCount) => {
|
||||||
|
state.unapprovedEncryptionPublicKeyMsgs = newMessages;
|
||||||
|
state.unapprovedEncryptionPublicKeyMsgCount = messageCount;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A getter for the number of 'unapproved' Messages in this.messages
|
||||||
|
*
|
||||||
|
* @returns The number of 'unapproved' Messages in this.messages
|
||||||
|
*/
|
||||||
|
get unapprovedMsgCount(): number {
|
||||||
|
return this._encryptionPublicKeyManager.getUnapprovedMessagesCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the controller state to the initial state.
|
||||||
|
*/
|
||||||
|
resetState() {
|
||||||
|
this.update(() => getDefaultState());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a Dapp uses the eth_getEncryptionPublicKey method, to request user approval.
|
||||||
|
*
|
||||||
|
* @param address - The address from the encryption public key will be extracted.
|
||||||
|
* @param [req] - The original request, containing the origin.
|
||||||
|
*/
|
||||||
|
async newRequestEncryptionPublicKey(
|
||||||
|
address: string,
|
||||||
|
req: OriginalRequest,
|
||||||
|
): Promise<string> {
|
||||||
|
const keyring = await this._keyringController.getKeyringForAccount(address);
|
||||||
|
|
||||||
|
switch (keyring.type) {
|
||||||
|
case KeyringType.ledger: {
|
||||||
|
return new Promise((_, reject) => {
|
||||||
|
reject(
|
||||||
|
new Error('Ledger does not support eth_getEncryptionPublicKey.'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
case KeyringType.trezor: {
|
||||||
|
return new Promise((_, reject) => {
|
||||||
|
reject(
|
||||||
|
new Error('Trezor does not support eth_getEncryptionPublicKey.'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
case KeyringType.lattice: {
|
||||||
|
return new Promise((_, reject) => {
|
||||||
|
reject(
|
||||||
|
new Error('Lattice does not support eth_getEncryptionPublicKey.'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
case KeyringType.qr: {
|
||||||
|
return Promise.reject(
|
||||||
|
new Error('QR hardware does not support eth_getEncryptionPublicKey.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
return this._encryptionPublicKeyManager.addUnapprovedMessageAsync(
|
||||||
|
{ from: address },
|
||||||
|
req,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signifies a user's approval to receiving encryption public key in queue.
|
||||||
|
*
|
||||||
|
* @param msgParams - The params of the message to receive & return to the Dapp.
|
||||||
|
* @returns A full state update.
|
||||||
|
*/
|
||||||
|
async encryptionPublicKey(msgParams: EncryptionPublicKeyParamsMetamask) {
|
||||||
|
log.info('MetaMaskController - encryptionPublicKey');
|
||||||
|
const messageId = msgParams.metamaskId as string;
|
||||||
|
// sets the status op the message to 'approved'
|
||||||
|
// and removes the metamaskId for decryption
|
||||||
|
try {
|
||||||
|
const cleanMessageParams =
|
||||||
|
await this._encryptionPublicKeyManager.approveMessage(msgParams);
|
||||||
|
|
||||||
|
// EncryptionPublicKey message
|
||||||
|
const publicKey = await this._keyringController.getEncryptionPublicKey(
|
||||||
|
cleanMessageParams.from,
|
||||||
|
);
|
||||||
|
|
||||||
|
// tells the listener that the message has been processed
|
||||||
|
// and can be returned to the dapp
|
||||||
|
this._encryptionPublicKeyManager.setMessageStatusAndResult(
|
||||||
|
messageId,
|
||||||
|
publicKey,
|
||||||
|
'received',
|
||||||
|
);
|
||||||
|
|
||||||
|
this._acceptApproval(messageId);
|
||||||
|
|
||||||
|
return this._getState();
|
||||||
|
} catch (error) {
|
||||||
|
log.info(
|
||||||
|
'MetaMaskController - eth_getEncryptionPublicKey failed.',
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
this._cancelAbstractMessage(this._encryptionPublicKeyManager, messageId);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to cancel a message submitted via eth_getEncryptionPublicKey.
|
||||||
|
*
|
||||||
|
* @param msgId - The id of the message to cancel.
|
||||||
|
*/
|
||||||
|
cancelEncryptionPublicKey(msgId: string) {
|
||||||
|
this._cancelAbstractMessage(this._encryptionPublicKeyManager, msgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reject all unapproved messages of any type.
|
||||||
|
*
|
||||||
|
* @param reason - A message to indicate why.
|
||||||
|
*/
|
||||||
|
rejectUnapproved(reason?: string) {
|
||||||
|
Object.keys(
|
||||||
|
this._encryptionPublicKeyManager.getUnapprovedMessages(),
|
||||||
|
).forEach((messageId) => {
|
||||||
|
this._cancelAbstractMessage(
|
||||||
|
this._encryptionPublicKeyManager,
|
||||||
|
messageId,
|
||||||
|
reason,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all unapproved messages from memory.
|
||||||
|
*/
|
||||||
|
clearUnapproved() {
|
||||||
|
this._encryptionPublicKeyManager.update({
|
||||||
|
unapprovedMessages: {},
|
||||||
|
unapprovedMessagesCount: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cancelAbstractMessage(
|
||||||
|
messageManager: AbstractMessageManager<
|
||||||
|
AbstractMessage,
|
||||||
|
AbstractMessageParams,
|
||||||
|
AbstractMessageParamsMetamask
|
||||||
|
>,
|
||||||
|
messageId: string,
|
||||||
|
reason?: string,
|
||||||
|
) {
|
||||||
|
if (reason) {
|
||||||
|
this._metricsEvent({
|
||||||
|
event: reason,
|
||||||
|
category: MetaMetricsEventCategory.Messages,
|
||||||
|
properties: {
|
||||||
|
action: 'Encryption public key Request',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
messageManager.rejectMessage(messageId);
|
||||||
|
this._rejectApproval(messageId);
|
||||||
|
|
||||||
|
return this._getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _subscribeToMessageState(
|
||||||
|
messageManager: AbstractMessageManager<
|
||||||
|
AbstractMessage,
|
||||||
|
AbstractMessageParams,
|
||||||
|
AbstractMessageParamsMetamask
|
||||||
|
>,
|
||||||
|
updateState: (
|
||||||
|
state: EncryptionPublicKeyControllerState,
|
||||||
|
newMessages: Record<string, StateMessage>,
|
||||||
|
messageCount: number,
|
||||||
|
) => void,
|
||||||
|
) {
|
||||||
|
messageManager.subscribe(
|
||||||
|
async (state: MessageManagerState<AbstractMessage>) => {
|
||||||
|
const newMessages = await this._migrateMessages(
|
||||||
|
state.unapprovedMessages as any,
|
||||||
|
);
|
||||||
|
this.update((draftState) => {
|
||||||
|
updateState(draftState, newMessages, state.unapprovedMessagesCount);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _migrateMessages(
|
||||||
|
coreMessages: Record<string, CoreMessage>,
|
||||||
|
): Promise<Record<string, StateMessage>> {
|
||||||
|
const stateMessages: Record<string, StateMessage> = {};
|
||||||
|
|
||||||
|
for (const messageId of Object.keys(coreMessages)) {
|
||||||
|
const coreMessage = coreMessages[messageId];
|
||||||
|
const stateMessage = await this._migrateMessage(coreMessage);
|
||||||
|
|
||||||
|
stateMessages[messageId] = stateMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _migrateMessage(
|
||||||
|
coreMessage: CoreMessage,
|
||||||
|
): Promise<StateMessage> {
|
||||||
|
const { messageParams, ...coreMessageData } = coreMessage;
|
||||||
|
|
||||||
|
// Core message managers use messageParams but frontend uses msgParams with lots of references
|
||||||
|
const stateMessage = {
|
||||||
|
...coreMessageData,
|
||||||
|
rawSig: coreMessage.rawSig as string,
|
||||||
|
msgParams: messageParams.from,
|
||||||
|
origin: messageParams.origin,
|
||||||
|
};
|
||||||
|
|
||||||
|
return stateMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _requestApproval(
|
||||||
|
msgParams: AbstractMessageParamsMetamask,
|
||||||
|
type: string,
|
||||||
|
) {
|
||||||
|
const id = msgParams.metamaskId as string;
|
||||||
|
const origin = msgParams.origin || ORIGIN_METAMASK;
|
||||||
|
|
||||||
|
this.messagingSystem
|
||||||
|
.call(
|
||||||
|
'ApprovalController:addRequest',
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
origin,
|
||||||
|
type,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.catch(() => {
|
||||||
|
// Intentionally ignored as promise not currently used
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _acceptApproval(messageId: string) {
|
||||||
|
this.messagingSystem.call('ApprovalController:acceptRequest', messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _rejectApproval(messageId: string) {
|
||||||
|
this.messagingSystem.call(
|
||||||
|
'ApprovalController:rejectRequest',
|
||||||
|
messageId,
|
||||||
|
'Cancel',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
export { default, NetworkControllerEventTypes } from './network-controller';
|
|
1
app/scripts/controllers/network/index.ts
Normal file
1
app/scripts/controllers/network/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './network-controller';
|
@ -1,679 +0,0 @@
|
|||||||
import { strict as assert } from 'assert';
|
|
||||||
import EventEmitter from 'events';
|
|
||||||
import { ComposedStore, ObservableStore } from '@metamask/obs-store';
|
|
||||||
import log from 'loglevel';
|
|
||||||
import {
|
|
||||||
createSwappableProxy,
|
|
||||||
createEventEmitterProxy,
|
|
||||||
} from '@metamask/swappable-obj-proxy';
|
|
||||||
import EthQuery from 'eth-query';
|
|
||||||
// ControllerMessenger is referred to in the JSDocs
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
import { ControllerMessenger } from '@metamask/base-controller';
|
|
||||||
import { v4 as random } from 'uuid';
|
|
||||||
import { hasProperty, isPlainObject } from '@metamask/utils';
|
|
||||||
import { errorCodes } from 'eth-rpc-errors';
|
|
||||||
import {
|
|
||||||
INFURA_PROVIDER_TYPES,
|
|
||||||
BUILT_IN_NETWORKS,
|
|
||||||
INFURA_BLOCKED_KEY,
|
|
||||||
TEST_NETWORK_TICKER_MAP,
|
|
||||||
CHAIN_IDS,
|
|
||||||
NETWORK_TYPES,
|
|
||||||
NetworkStatus,
|
|
||||||
} from '../../../../shared/constants/network';
|
|
||||||
import {
|
|
||||||
isPrefixedFormattedHexString,
|
|
||||||
isSafeChainId,
|
|
||||||
} from '../../../../shared/modules/network.utils';
|
|
||||||
import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics';
|
|
||||||
import { createNetworkClient } from './create-network-client';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} NetworkConfiguration
|
|
||||||
* @property {string} rpcUrl - RPC target URL.
|
|
||||||
* @property {string} chainId - Network ID as per EIP-155
|
|
||||||
* @property {string} ticker - Currency ticker.
|
|
||||||
* @property {object} [rpcPrefs] - Personalized preferences.
|
|
||||||
* @property {string} [nickname] - Personalized network name.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function buildDefaultProviderConfigState() {
|
|
||||||
if (process.env.IN_TEST) {
|
|
||||||
return {
|
|
||||||
type: NETWORK_TYPES.RPC,
|
|
||||||
rpcUrl: 'http://localhost:8545',
|
|
||||||
chainId: '0x539',
|
|
||||||
nickname: 'Localhost 8545',
|
|
||||||
ticker: 'ETH',
|
|
||||||
};
|
|
||||||
} else if (
|
|
||||||
process.env.METAMASK_DEBUG ||
|
|
||||||
process.env.METAMASK_ENV === 'test'
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
type: NETWORK_TYPES.GOERLI,
|
|
||||||
chainId: CHAIN_IDS.GOERLI,
|
|
||||||
ticker: TEST_NETWORK_TICKER_MAP.GOERLI,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: NETWORK_TYPES.MAINNET,
|
|
||||||
chainId: CHAIN_IDS.MAINNET,
|
|
||||||
ticker: 'ETH',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildDefaultNetworkIdState() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildDefaultNetworkStatusState() {
|
|
||||||
return NetworkStatus.Unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildDefaultNetworkDetailsState() {
|
|
||||||
return {
|
|
||||||
EIPS: {
|
|
||||||
1559: undefined,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildDefaultNetworkConfigurationsState() {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the controller.
|
|
||||||
*/
|
|
||||||
const name = 'NetworkController';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The set of event types that this controller can publish via its messenger.
|
|
||||||
*/
|
|
||||||
export const NetworkControllerEventTypes = {
|
|
||||||
/**
|
|
||||||
* Fired after the current network is changed.
|
|
||||||
*/
|
|
||||||
NetworkDidChange: `${name}:networkDidChange`,
|
|
||||||
/**
|
|
||||||
* Fired when there is a request to change the current network, but no state
|
|
||||||
* changes have occurred yet.
|
|
||||||
*/
|
|
||||||
NetworkWillChange: `${name}:networkWillChange`,
|
|
||||||
/**
|
|
||||||
* Fired after the network is changed to an Infura network, but when Infura
|
|
||||||
* returns an error denying support for the user's location.
|
|
||||||
*/
|
|
||||||
InfuraIsBlocked: `${name}:infuraIsBlocked`,
|
|
||||||
/**
|
|
||||||
* Fired after the network is changed to an Infura network and Infura does not
|
|
||||||
* return an error denying support for the user's location, or after the
|
|
||||||
* network is changed to a custom network.
|
|
||||||
*/
|
|
||||||
InfuraIsUnblocked: `${name}:infuraIsUnblocked`,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class NetworkController extends EventEmitter {
|
|
||||||
/**
|
|
||||||
* Construct a NetworkController.
|
|
||||||
*
|
|
||||||
* @param {object} options - Options for this controller.
|
|
||||||
* @param {ControllerMessenger} options.messenger - The controller messenger.
|
|
||||||
* @param {object} [options.state] - Initial controller state.
|
|
||||||
* @param {string} [options.infuraProjectId] - The Infura project ID.
|
|
||||||
* @param {string} [options.trackMetaMetricsEvent] - A method to forward events to the MetaMetricsController
|
|
||||||
*/
|
|
||||||
constructor({
|
|
||||||
messenger,
|
|
||||||
state = {},
|
|
||||||
infuraProjectId,
|
|
||||||
trackMetaMetricsEvent,
|
|
||||||
} = {}) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.messenger = messenger;
|
|
||||||
|
|
||||||
// create stores
|
|
||||||
this.providerStore = new ObservableStore(
|
|
||||||
state.provider || buildDefaultProviderConfigState(),
|
|
||||||
);
|
|
||||||
this.previousProviderStore = new ObservableStore(
|
|
||||||
this.providerStore.getState(),
|
|
||||||
);
|
|
||||||
this.networkIdStore = new ObservableStore(buildDefaultNetworkIdState());
|
|
||||||
this.networkStatusStore = new ObservableStore(
|
|
||||||
buildDefaultNetworkStatusState(),
|
|
||||||
);
|
|
||||||
// We need to keep track of a few details about the current network.
|
|
||||||
// Ideally we'd merge this.networkStatusStore with this new store, but doing
|
|
||||||
// so will require a decent sized refactor of how we're accessing network
|
|
||||||
// state. Currently this is only used for detecting EIP-1559 support but can
|
|
||||||
// be extended to track other network details.
|
|
||||||
this.networkDetails = new ObservableStore(
|
|
||||||
state.networkDetails || buildDefaultNetworkDetailsState(),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.networkConfigurationsStore = new ObservableStore(
|
|
||||||
state.networkConfigurations || buildDefaultNetworkConfigurationsState(),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.store = new ComposedStore({
|
|
||||||
provider: this.providerStore,
|
|
||||||
previousProviderStore: this.previousProviderStore,
|
|
||||||
networkId: this.networkIdStore,
|
|
||||||
networkStatus: this.networkStatusStore,
|
|
||||||
networkDetails: this.networkDetails,
|
|
||||||
networkConfigurations: this.networkConfigurationsStore,
|
|
||||||
});
|
|
||||||
|
|
||||||
// provider and block tracker
|
|
||||||
this._provider = null;
|
|
||||||
this._blockTracker = null;
|
|
||||||
|
|
||||||
// provider and block tracker proxies - because the network changes
|
|
||||||
this._providerProxy = null;
|
|
||||||
this._blockTrackerProxy = null;
|
|
||||||
|
|
||||||
if (!infuraProjectId || typeof infuraProjectId !== 'string') {
|
|
||||||
throw new Error('Invalid Infura project ID');
|
|
||||||
}
|
|
||||||
this._infuraProjectId = infuraProjectId;
|
|
||||||
|
|
||||||
this._trackMetaMetricsEvent = trackMetaMetricsEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the network controller, stopping any ongoing polling.
|
|
||||||
*
|
|
||||||
* In-progress requests will not be aborted.
|
|
||||||
*/
|
|
||||||
async destroy() {
|
|
||||||
await this._blockTracker?.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
async initializeProvider() {
|
|
||||||
const { type, rpcUrl, chainId } = this.providerStore.getState();
|
|
||||||
this._configureProvider({ type, rpcUrl, chainId });
|
|
||||||
await this.lookupNetwork();
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the proxies so the references will always be good
|
|
||||||
getProviderAndBlockTracker() {
|
|
||||||
const provider = this._providerProxy;
|
|
||||||
const blockTracker = this._blockTrackerProxy;
|
|
||||||
return { provider, blockTracker };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether the network supports EIP-1559 by checking whether the
|
|
||||||
* latest block has a `baseFeePerGas` property, then updates state
|
|
||||||
* appropriately.
|
|
||||||
*
|
|
||||||
* @returns {Promise<boolean>} A promise that resolves to true if the network
|
|
||||||
* supports EIP-1559 and false otherwise.
|
|
||||||
*/
|
|
||||||
async getEIP1559Compatibility() {
|
|
||||||
const { EIPS } = this.networkDetails.getState();
|
|
||||||
// NOTE: This isn't necessary anymore because the block cache middleware
|
|
||||||
// already prevents duplicate requests from taking place
|
|
||||||
if (EIPS[1559] !== undefined) {
|
|
||||||
return EIPS[1559];
|
|
||||||
}
|
|
||||||
const supportsEIP1559 = await this._determineEIP1559Compatibility();
|
|
||||||
this.networkDetails.updateState({
|
|
||||||
EIPS: {
|
|
||||||
...this.networkDetails.getState().EIPS,
|
|
||||||
1559: supportsEIP1559,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return supportsEIP1559;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Captures information about the currently selected network — namely,
|
|
||||||
* the network ID and whether the network supports EIP-1559 — and then uses
|
|
||||||
* the results of these requests to determine the status of the network.
|
|
||||||
*/
|
|
||||||
async lookupNetwork() {
|
|
||||||
const { chainId, type } = this.providerStore.getState();
|
|
||||||
let networkChanged = false;
|
|
||||||
let networkId;
|
|
||||||
let supportsEIP1559;
|
|
||||||
let networkStatus;
|
|
||||||
|
|
||||||
if (!this._provider) {
|
|
||||||
log.warn(
|
|
||||||
'NetworkController - lookupNetwork aborted due to missing provider',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chainId) {
|
|
||||||
log.warn(
|
|
||||||
'NetworkController - lookupNetwork aborted due to missing chainId',
|
|
||||||
);
|
|
||||||
this._resetNetworkId();
|
|
||||||
this._resetNetworkStatus();
|
|
||||||
this._resetNetworkDetails();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isInfura = INFURA_PROVIDER_TYPES.includes(type);
|
|
||||||
|
|
||||||
const listener = () => {
|
|
||||||
networkChanged = true;
|
|
||||||
this.messenger.unsubscribe(
|
|
||||||
NetworkControllerEventTypes.NetworkDidChange,
|
|
||||||
listener,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
this.messenger.subscribe(
|
|
||||||
NetworkControllerEventTypes.NetworkDidChange,
|
|
||||||
listener,
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const results = await Promise.all([
|
|
||||||
this._getNetworkId(),
|
|
||||||
this._determineEIP1559Compatibility(),
|
|
||||||
]);
|
|
||||||
networkId = results[0];
|
|
||||||
supportsEIP1559 = results[1];
|
|
||||||
networkStatus = NetworkStatus.Available;
|
|
||||||
} catch (error) {
|
|
||||||
if (hasProperty(error, 'code')) {
|
|
||||||
let responseBody;
|
|
||||||
try {
|
|
||||||
responseBody = JSON.parse(error.message);
|
|
||||||
} catch {
|
|
||||||
// error.message must not be JSON
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
isPlainObject(responseBody) &&
|
|
||||||
responseBody.error === INFURA_BLOCKED_KEY
|
|
||||||
) {
|
|
||||||
networkStatus = NetworkStatus.Blocked;
|
|
||||||
} else if (error.code === errorCodes.rpc.internal) {
|
|
||||||
networkStatus = NetworkStatus.Unknown;
|
|
||||||
} else {
|
|
||||||
networkStatus = NetworkStatus.Unavailable;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.warn(
|
|
||||||
'NetworkController - could not determine network status',
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
networkStatus = NetworkStatus.Unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (networkChanged) {
|
|
||||||
// If the network has changed, then `lookupNetwork` either has been or is
|
|
||||||
// in the process of being called, so we don't need to go further.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.messenger.unsubscribe(
|
|
||||||
NetworkControllerEventTypes.NetworkDidChange,
|
|
||||||
listener,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.networkStatusStore.putState(networkStatus);
|
|
||||||
|
|
||||||
if (networkStatus === NetworkStatus.Available) {
|
|
||||||
this.networkIdStore.putState(networkId);
|
|
||||||
this.networkDetails.updateState({
|
|
||||||
EIPS: {
|
|
||||||
...this.networkDetails.getState().EIPS,
|
|
||||||
1559: supportsEIP1559,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this._resetNetworkId();
|
|
||||||
this._resetNetworkDetails();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInfura) {
|
|
||||||
if (networkStatus === NetworkStatus.Available) {
|
|
||||||
this.messenger.publish(NetworkControllerEventTypes.InfuraIsUnblocked);
|
|
||||||
} else if (networkStatus === NetworkStatus.Blocked) {
|
|
||||||
this.messenger.publish(NetworkControllerEventTypes.InfuraIsBlocked);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Always publish infuraIsUnblocked regardless of network status to
|
|
||||||
// prevent consumers from being stuck in a blocked state if they were
|
|
||||||
// previously connected to an Infura network that was blocked
|
|
||||||
this.messenger.publish(NetworkControllerEventTypes.InfuraIsUnblocked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A method for setting the currently selected network provider by networkConfigurationId.
|
|
||||||
*
|
|
||||||
* @param {string} networkConfigurationId - the universal unique identifier that corresponds to the network configuration to set as active.
|
|
||||||
* @returns {string} The rpcUrl of the network that was just set as active
|
|
||||||
*/
|
|
||||||
setActiveNetwork(networkConfigurationId) {
|
|
||||||
const targetNetwork =
|
|
||||||
this.networkConfigurationsStore.getState()[networkConfigurationId];
|
|
||||||
|
|
||||||
if (!targetNetwork) {
|
|
||||||
throw new Error(
|
|
||||||
`networkConfigurationId ${networkConfigurationId} does not match a configured networkConfiguration`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._setProviderConfig({
|
|
||||||
type: NETWORK_TYPES.RPC,
|
|
||||||
...targetNetwork,
|
|
||||||
});
|
|
||||||
|
|
||||||
return targetNetwork.rpcUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
setProviderType(type) {
|
|
||||||
assert.notStrictEqual(
|
|
||||||
type,
|
|
||||||
NETWORK_TYPES.RPC,
|
|
||||||
`NetworkController - cannot call "setProviderType" with type "${NETWORK_TYPES.RPC}". Use "setActiveNetwork"`,
|
|
||||||
);
|
|
||||||
assert.ok(
|
|
||||||
INFURA_PROVIDER_TYPES.includes(type),
|
|
||||||
`Unknown Infura provider type "${type}".`,
|
|
||||||
);
|
|
||||||
const { chainId, ticker, blockExplorerUrl } = BUILT_IN_NETWORKS[type];
|
|
||||||
this._setProviderConfig({
|
|
||||||
type,
|
|
||||||
rpcUrl: '',
|
|
||||||
chainId,
|
|
||||||
ticker: ticker ?? 'ETH',
|
|
||||||
nickname: '',
|
|
||||||
rpcPrefs: { blockExplorerUrl },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
resetConnection() {
|
|
||||||
this._setProviderConfig(this.providerStore.getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
rollbackToPreviousProvider() {
|
|
||||||
const config = this.previousProviderStore.getState();
|
|
||||||
this.providerStore.putState(config);
|
|
||||||
this._switchNetwork(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Private
|
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method to return the latest block for the current network
|
|
||||||
*
|
|
||||||
* @returns {object} Block header
|
|
||||||
*/
|
|
||||||
_getLatestBlock() {
|
|
||||||
const { provider } = this.getProviderAndBlockTracker();
|
|
||||||
const ethQuery = new EthQuery(provider);
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
ethQuery.sendAsync(
|
|
||||||
{ method: 'eth_getBlockByNumber', params: ['latest', false] },
|
|
||||||
(error, result) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error);
|
|
||||||
} else {
|
|
||||||
resolve(result);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the network ID for the current selected network
|
|
||||||
*
|
|
||||||
* @returns {string} The network ID for the current network.
|
|
||||||
*/
|
|
||||||
async _getNetworkId() {
|
|
||||||
const { provider } = this.getProviderAndBlockTracker();
|
|
||||||
const ethQuery = new EthQuery(provider);
|
|
||||||
|
|
||||||
return await new Promise((resolve, reject) => {
|
|
||||||
ethQuery.sendAsync({ method: 'net_version' }, (error, result) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error);
|
|
||||||
} else {
|
|
||||||
resolve(result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the stored network ID.
|
|
||||||
*/
|
|
||||||
_resetNetworkId() {
|
|
||||||
this.networkIdStore.putState(buildDefaultNetworkIdState());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets network status to the default ("unknown").
|
|
||||||
*/
|
|
||||||
_resetNetworkStatus() {
|
|
||||||
this.networkStatusStore.putState(buildDefaultNetworkStatusState());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears details previously stored for the network.
|
|
||||||
*/
|
|
||||||
_resetNetworkDetails() {
|
|
||||||
this.networkDetails.putState(buildDefaultNetworkDetailsState());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the provider config and switches the network.
|
|
||||||
*
|
|
||||||
* @param config
|
|
||||||
*/
|
|
||||||
_setProviderConfig(config) {
|
|
||||||
this.previousProviderStore.putState(this.providerStore.getState());
|
|
||||||
this.providerStore.putState(config);
|
|
||||||
this._switchNetwork(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the latest block from the currently selected network; if the
|
|
||||||
* block has a `baseFeePerGas` property, then we know that the network
|
|
||||||
* supports EIP-1559; otherwise it doesn't.
|
|
||||||
*
|
|
||||||
* @returns {Promise<boolean>} A promise that resolves to true if the network
|
|
||||||
* supports EIP-1559 and false otherwise.
|
|
||||||
*/
|
|
||||||
async _determineEIP1559Compatibility() {
|
|
||||||
const latestBlock = await this._getLatestBlock();
|
|
||||||
return latestBlock && latestBlock.baseFeePerGas !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
_switchNetwork(opts) {
|
|
||||||
this.messenger.publish(NetworkControllerEventTypes.NetworkWillChange);
|
|
||||||
this._resetNetworkId();
|
|
||||||
this._resetNetworkStatus();
|
|
||||||
this._resetNetworkDetails();
|
|
||||||
this._configureProvider(opts);
|
|
||||||
this.messenger.publish(NetworkControllerEventTypes.NetworkDidChange);
|
|
||||||
this.lookupNetwork();
|
|
||||||
}
|
|
||||||
|
|
||||||
_configureProvider({ type, rpcUrl, chainId }) {
|
|
||||||
// infura type-based endpoints
|
|
||||||
const isInfura = INFURA_PROVIDER_TYPES.includes(type);
|
|
||||||
if (isInfura) {
|
|
||||||
this._configureInfuraProvider({
|
|
||||||
type,
|
|
||||||
infuraProjectId: this._infuraProjectId,
|
|
||||||
});
|
|
||||||
// url-based rpc endpoints
|
|
||||||
} else if (type === NETWORK_TYPES.RPC) {
|
|
||||||
this._configureStandardProvider(rpcUrl, chainId);
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
`NetworkController - _configureProvider - unknown type "${type}"`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_configureInfuraProvider({ type, infuraProjectId }) {
|
|
||||||
log.info('NetworkController - configureInfuraProvider', type);
|
|
||||||
const { provider, blockTracker } = createNetworkClient({
|
|
||||||
network: type,
|
|
||||||
infuraProjectId,
|
|
||||||
type: 'infura',
|
|
||||||
});
|
|
||||||
this._setProviderAndBlockTracker({ provider, blockTracker });
|
|
||||||
}
|
|
||||||
|
|
||||||
_configureStandardProvider(rpcUrl, chainId) {
|
|
||||||
log.info('NetworkController - configureStandardProvider', rpcUrl);
|
|
||||||
const { provider, blockTracker } = createNetworkClient({
|
|
||||||
chainId,
|
|
||||||
rpcUrl,
|
|
||||||
type: 'custom',
|
|
||||||
});
|
|
||||||
this._setProviderAndBlockTracker({ provider, blockTracker });
|
|
||||||
}
|
|
||||||
|
|
||||||
_setProviderAndBlockTracker({ provider, blockTracker }) {
|
|
||||||
// update or initialize proxies
|
|
||||||
if (this._providerProxy) {
|
|
||||||
this._providerProxy.setTarget(provider);
|
|
||||||
} else {
|
|
||||||
this._providerProxy = createSwappableProxy(provider);
|
|
||||||
}
|
|
||||||
if (this._blockTrackerProxy) {
|
|
||||||
this._blockTrackerProxy.setTarget(blockTracker);
|
|
||||||
} else {
|
|
||||||
this._blockTrackerProxy = createEventEmitterProxy(blockTracker, {
|
|
||||||
eventFilter: 'skipInternal',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// set new provider and blockTracker
|
|
||||||
this._provider = provider;
|
|
||||||
this._blockTracker = blockTracker;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Network Configuration management functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a network configuration if the rpcUrl is not already present on an
|
|
||||||
* existing network configuration. Otherwise updates the entry with the matching rpcUrl.
|
|
||||||
*
|
|
||||||
* @param {NetworkConfiguration} networkConfiguration - The network configuration to add or, if rpcUrl matches an existing entry, to modify.
|
|
||||||
* @param {object} options
|
|
||||||
* @param {boolean} options.setActive - An option to set the newly added networkConfiguration as the active provider.
|
|
||||||
* @param {string} options.referrer - The site from which the call originated, or 'metamask' for internal calls - used for event metrics.
|
|
||||||
* @param {string} options.source - Where the upsertNetwork event originated (i.e. from a dapp or from the network form)- used for event metrics.
|
|
||||||
* @returns {string} id for the added or updated network configuration
|
|
||||||
*/
|
|
||||||
upsertNetworkConfiguration(
|
|
||||||
{ rpcUrl, chainId, ticker, nickname, rpcPrefs },
|
|
||||||
{ setActive = false, referrer, source },
|
|
||||||
) {
|
|
||||||
assert.ok(
|
|
||||||
isPrefixedFormattedHexString(chainId),
|
|
||||||
`Invalid chain ID "${chainId}": invalid hex string.`,
|
|
||||||
);
|
|
||||||
assert.ok(
|
|
||||||
isSafeChainId(parseInt(chainId, 16)),
|
|
||||||
`Invalid chain ID "${chainId}": numerical value greater than max safe value.`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!rpcUrl) {
|
|
||||||
throw new Error(
|
|
||||||
'An rpcUrl is required to add or update network configuration',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!referrer || !source) {
|
|
||||||
throw new Error(
|
|
||||||
'referrer and source are required arguments for adding or updating a network configuration',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// eslint-disable-next-line no-new
|
|
||||||
new URL(rpcUrl);
|
|
||||||
} catch (e) {
|
|
||||||
if (e.message.includes('Invalid URL')) {
|
|
||||||
throw new Error('rpcUrl must be a valid URL');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ticker) {
|
|
||||||
throw new Error(
|
|
||||||
'A ticker is required to add or update networkConfiguration',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const networkConfigurations = this.networkConfigurationsStore.getState();
|
|
||||||
const newNetworkConfiguration = {
|
|
||||||
rpcUrl,
|
|
||||||
chainId,
|
|
||||||
ticker,
|
|
||||||
nickname,
|
|
||||||
rpcPrefs,
|
|
||||||
};
|
|
||||||
|
|
||||||
const oldNetworkConfigurationId = Object.values(networkConfigurations).find(
|
|
||||||
(networkConfiguration) =>
|
|
||||||
networkConfiguration.rpcUrl?.toLowerCase() === rpcUrl?.toLowerCase(),
|
|
||||||
)?.id;
|
|
||||||
|
|
||||||
const newNetworkConfigurationId = oldNetworkConfigurationId || random();
|
|
||||||
this.networkConfigurationsStore.putState({
|
|
||||||
...networkConfigurations,
|
|
||||||
[newNetworkConfigurationId]: {
|
|
||||||
...newNetworkConfiguration,
|
|
||||||
id: newNetworkConfigurationId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!oldNetworkConfigurationId) {
|
|
||||||
this._trackMetaMetricsEvent({
|
|
||||||
event: 'Custom Network Added',
|
|
||||||
category: MetaMetricsEventCategory.Network,
|
|
||||||
referrer: {
|
|
||||||
url: referrer,
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
chain_id: chainId,
|
|
||||||
symbol: ticker,
|
|
||||||
source,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setActive) {
|
|
||||||
this.setActiveNetwork(newNetworkConfigurationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newNetworkConfigurationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes network configuration from state.
|
|
||||||
*
|
|
||||||
* @param {string} networkConfigurationId - the unique id for the network configuration to remove.
|
|
||||||
*/
|
|
||||||
removeNetworkConfiguration(networkConfigurationId) {
|
|
||||||
const networkConfigurations = {
|
|
||||||
...this.networkConfigurationsStore.getState(),
|
|
||||||
};
|
|
||||||
delete networkConfigurations[networkConfigurationId];
|
|
||||||
this.networkConfigurationsStore.putState(networkConfigurations);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
1168
app/scripts/controllers/network/network-controller.ts
Normal file
1168
app/scripts/controllers/network/network-controller.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,15 @@
|
|||||||
/* eslint-disable jest/require-top-level-describe, jest/no-export */
|
/* eslint-disable jest/require-top-level-describe, jest/no-export */
|
||||||
|
|
||||||
import { withMockedCommunications, withNetworkClient } from './helpers';
|
import {
|
||||||
|
ProviderType,
|
||||||
|
withMockedCommunications,
|
||||||
|
withNetworkClient,
|
||||||
|
} from './helpers';
|
||||||
|
|
||||||
|
type TestsForRpcMethodThatCheckForBlockHashInResponseOptions = {
|
||||||
|
providerType: ProviderType;
|
||||||
|
numberOfParameters: number;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines tests which exercise the behavior exhibited by an RPC method that
|
* Defines tests which exercise the behavior exhibited by an RPC method that
|
||||||
@ -15,8 +24,11 @@ import { withMockedCommunications, withNetworkClient } from './helpers';
|
|||||||
* either `infura` or `custom` (default: "infura").
|
* either `infura` or `custom` (default: "infura").
|
||||||
*/
|
*/
|
||||||
export function testsForRpcMethodsThatCheckForBlockHashInResponse(
|
export function testsForRpcMethodsThatCheckForBlockHashInResponse(
|
||||||
method,
|
method: string,
|
||||||
{ numberOfParameters, providerType },
|
{
|
||||||
|
numberOfParameters,
|
||||||
|
providerType,
|
||||||
|
}: TestsForRpcMethodThatCheckForBlockHashInResponseOptions,
|
||||||
) {
|
) {
|
||||||
if (providerType !== 'infura' && providerType !== 'custom') {
|
if (providerType !== 'infura' && providerType !== 'custom') {
|
||||||
throw new Error(
|
throw new Error(
|
@ -3,6 +3,7 @@
|
|||||||
import {
|
import {
|
||||||
buildMockParams,
|
buildMockParams,
|
||||||
buildRequestWithReplacedBlockParam,
|
buildRequestWithReplacedBlockParam,
|
||||||
|
ProviderType,
|
||||||
waitForPromiseToBeFulfilledAfterRunningAllTimers,
|
waitForPromiseToBeFulfilledAfterRunningAllTimers,
|
||||||
withMockedCommunications,
|
withMockedCommunications,
|
||||||
withNetworkClient,
|
withNetworkClient,
|
||||||
@ -13,6 +14,12 @@ import {
|
|||||||
buildJsonRpcEngineEmptyResponseErrorMessage,
|
buildJsonRpcEngineEmptyResponseErrorMessage,
|
||||||
} from './shared-tests';
|
} from './shared-tests';
|
||||||
|
|
||||||
|
type TestsForRpcMethodSupportingBlockParam = {
|
||||||
|
providerType: ProviderType;
|
||||||
|
blockParamIndex: number;
|
||||||
|
numberOfParameters: number;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines tests which exercise the behavior exhibited by an RPC method that
|
* Defines tests which exercise the behavior exhibited by an RPC method that
|
||||||
* takes a block parameter. The value of this parameter can be either a block
|
* takes a block parameter. The value of this parameter can be either a block
|
||||||
@ -28,8 +35,12 @@ import {
|
|||||||
*/
|
*/
|
||||||
/* eslint-disable-next-line jest/no-export */
|
/* eslint-disable-next-line jest/no-export */
|
||||||
export function testsForRpcMethodSupportingBlockParam(
|
export function testsForRpcMethodSupportingBlockParam(
|
||||||
method,
|
method: string,
|
||||||
{ blockParamIndex, numberOfParameters, providerType },
|
{
|
||||||
|
blockParamIndex,
|
||||||
|
numberOfParameters,
|
||||||
|
providerType,
|
||||||
|
}: TestsForRpcMethodSupportingBlockParam,
|
||||||
) {
|
) {
|
||||||
describe.each([
|
describe.each([
|
||||||
['given no block tag', undefined],
|
['given no block tag', undefined],
|
||||||
@ -1718,9 +1729,9 @@ export function testsForRpcMethodSupportingBlockParam(
|
|||||||
[
|
[
|
||||||
['less than the current block number', '0x200'],
|
['less than the current block number', '0x200'],
|
||||||
['equal to the curent block number', '0x100'],
|
['equal to the curent block number', '0x100'],
|
||||||
],
|
] as any,
|
||||||
'%s',
|
'%s',
|
||||||
(_nestedDesc, currentBlockNumber) => {
|
(_nestedDesc: string, currentBlockNumber: string) => {
|
||||||
it('makes an additional request to the RPC endpoint', async () => {
|
it('makes an additional request to the RPC endpoint', async () => {
|
||||||
await withMockedCommunications({ providerType }, async (comms) => {
|
await withMockedCommunications({ providerType }, async (comms) => {
|
||||||
const request = {
|
const request = {
|
@ -1,14 +1,13 @@
|
|||||||
import nock from 'nock';
|
import nock, { Scope as NockScope } from 'nock';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
import type { JSONRPCResponse } from '@json-rpc-specification/meta-schema';
|
||||||
import EthQuery from 'eth-query';
|
import EthQuery from 'eth-query';
|
||||||
import { createNetworkClient } from '../create-network-client';
|
import { Hex } from '@metamask/utils';
|
||||||
|
import { BuiltInInfuraNetwork } from '../../../../../shared/constants/network';
|
||||||
/**
|
import {
|
||||||
* @typedef {import('nock').Scope} NockScope
|
createNetworkClient,
|
||||||
*
|
NetworkClientType,
|
||||||
* A object returned by the `nock` function for mocking requests to a particular
|
} from '../create-network-client';
|
||||||
* base URL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dummy value for the `infuraProjectId` option that `createInfuraClient`
|
* A dummy value for the `infuraProjectId` option that `createInfuraClient`
|
||||||
@ -41,9 +40,9 @@ const originalSetTimeout = setTimeout;
|
|||||||
* keeps failing, you can set `process.env.DEBUG_PROVIDER_TESTS` to `1`. This
|
* keeps failing, you can set `process.env.DEBUG_PROVIDER_TESTS` to `1`. This
|
||||||
* will turn on some extra logging.
|
* will turn on some extra logging.
|
||||||
*
|
*
|
||||||
* @param {any[]} args - The arguments that `console.log` takes.
|
* @param args - The arguments that `console.log` takes.
|
||||||
*/
|
*/
|
||||||
function debug(...args) {
|
function debug(...args: any) {
|
||||||
if (process.env.DEBUG_PROVIDER_TESTS === '1') {
|
if (process.env.DEBUG_PROVIDER_TESTS === '1') {
|
||||||
console.log(...args);
|
console.log(...args);
|
||||||
}
|
}
|
||||||
@ -52,96 +51,89 @@ function debug(...args) {
|
|||||||
/**
|
/**
|
||||||
* Builds a Nock scope object for mocking provider requests.
|
* Builds a Nock scope object for mocking provider requests.
|
||||||
*
|
*
|
||||||
* @param {string} rpcUrl - The URL of the RPC endpoint.
|
* @param rpcUrl - The URL of the RPC endpoint.
|
||||||
* @returns {NockScope} The nock scope.
|
* @returns The nock scope.
|
||||||
*/
|
*/
|
||||||
function buildScopeForMockingRequests(rpcUrl) {
|
function buildScopeForMockingRequests(rpcUrl: string): NockScope {
|
||||||
return nock(rpcUrl).filteringRequestBody((body) => {
|
return nock(rpcUrl).filteringRequestBody((body) => {
|
||||||
debug('Nock Received Request: ', body);
|
debug('Nock Received Request: ', body);
|
||||||
return body;
|
return body;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
type Request = { method: string; params?: any[] };
|
||||||
* @typedef {{ nockScope: NockScope, blockNumber: string }} MockBlockTrackerRequestOptions
|
type Response = {
|
||||||
*
|
id?: number | string;
|
||||||
* The options to `mockNextBlockTrackerRequest` and `mockAllBlockTrackerRequests`.
|
jsonrpc?: '2.0';
|
||||||
|
error?: any;
|
||||||
|
result?: any;
|
||||||
|
httpStatus?: number;
|
||||||
|
};
|
||||||
|
type ResponseBody = { body: JSONRPCResponse };
|
||||||
|
type BodyOrResponse = ResponseBody | Response;
|
||||||
|
type CurriedMockRpcCallOptions = {
|
||||||
|
request: Request;
|
||||||
|
// The response data.
|
||||||
|
response?: BodyOrResponse;
|
||||||
|
/**
|
||||||
|
* An error to throw while making the request.
|
||||||
|
* Takes precedence over `response`.
|
||||||
*/
|
*/
|
||||||
|
error?: Error | string;
|
||||||
|
/**
|
||||||
|
* The amount of time that should pass before the
|
||||||
|
* request resolves with the response.
|
||||||
|
*/
|
||||||
|
delay?: number;
|
||||||
|
/**
|
||||||
|
* The number of times that the request is
|
||||||
|
* expected to be made.
|
||||||
|
*/
|
||||||
|
times?: number;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
type MockRpcCallOptions = {
|
||||||
* Mocks the next request for the latest block that the block tracker will make.
|
// A nock scope (a set of mocked requests scoped to a certain base URL).
|
||||||
*
|
nockScope: nock.Scope;
|
||||||
* @param {MockBlockTrackerRequestOptions} args - The arguments.
|
} & CurriedMockRpcCallOptions;
|
||||||
* @param {NockScope} args.nockScope - A nock scope (a set of mocked requests
|
|
||||||
* scoped to a certain base URL).
|
|
||||||
* @param {string} args.blockNumber - The block number that the block tracker
|
|
||||||
* should report, as a 0x-prefixed hex string.
|
|
||||||
*/
|
|
||||||
async function mockNextBlockTrackerRequest({
|
|
||||||
nockScope,
|
|
||||||
blockNumber = DEFAULT_LATEST_BLOCK_NUMBER,
|
|
||||||
}) {
|
|
||||||
await mockRpcCall({
|
|
||||||
nockScope,
|
|
||||||
request: { method: 'eth_blockNumber', params: [] },
|
|
||||||
response: { result: blockNumber },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
type MockRpcCallResult = nock.Interceptor | nock.Scope;
|
||||||
* Mocks all requests for the latest block that the block tracker will make.
|
|
||||||
*
|
|
||||||
* @param {MockBlockTrackerRequestOptions} args - The arguments.
|
|
||||||
* @param {NockScope} args.nockScope - A nock scope (a set of mocked requests
|
|
||||||
* scoped to a certain base URL).
|
|
||||||
* @param {string} args.blockNumber - The block number that the block tracker
|
|
||||||
* should report, as a 0x-prefixed hex string.
|
|
||||||
*/
|
|
||||||
async function mockAllBlockTrackerRequests({
|
|
||||||
nockScope,
|
|
||||||
blockNumber = DEFAULT_LATEST_BLOCK_NUMBER,
|
|
||||||
}) {
|
|
||||||
await mockRpcCall({
|
|
||||||
nockScope,
|
|
||||||
request: { method: 'eth_blockNumber', params: [] },
|
|
||||||
response: { result: blockNumber },
|
|
||||||
}).persist();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {{ nockScope: NockScope, request: object, response: object, delay?: number }} MockRpcCallOptions
|
|
||||||
*
|
|
||||||
* The options to `mockRpcCall`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mocks a JSON-RPC request sent to the provider with the given response.
|
* Mocks a JSON-RPC request sent to the provider with the given response.
|
||||||
* Provider type is inferred from the base url set on the nockScope.
|
* Provider type is inferred from the base url set on the nockScope.
|
||||||
*
|
*
|
||||||
* @param {MockRpcCallOptions} args - The arguments.
|
* @param args - The arguments.
|
||||||
* @param {NockScope} args.nockScope - A nock scope (a set of mocked requests
|
* @param args.nockScope - A nock scope (a set of mocked requests scoped to a
|
||||||
* scoped to a certain base URL).
|
* certain base URL).
|
||||||
* @param {object} args.request - The request data.
|
* @param args.request - The request data.
|
||||||
* @param {{body: string} | {httpStatus?: number; id?: number; method?: string; params?: string[]}} [args.response] - Information
|
* @param args.response - Information concerning the response that the request
|
||||||
* concerning the response that the request should have. If a `body` property is
|
* should have. If a `body` property is present, this is taken as the complete
|
||||||
* present, this is taken as the complete response body. If an `httpStatus`
|
* response body. If an `httpStatus` property is present, then it is taken as
|
||||||
* property is present, then it is taken as the HTTP status code to respond
|
* the HTTP status code to respond with. Properties other than these two are
|
||||||
* with. Properties other than these two are used to build a complete response
|
* used to build a complete response body (including `id` and `jsonrpc`
|
||||||
* body (including `id` and `jsonrpc` properties).
|
* properties).
|
||||||
* @param {Error | string} [args.error] - An error to throw while making the
|
* @param args.error - An error to throw while making the request. Takes
|
||||||
* request. Takes precedence over `response`.
|
* precedence over `response`.
|
||||||
* @param {number} [args.delay] - The amount of time that should pass before the
|
* @param args.delay - The amount of time that should pass before the request
|
||||||
* request resolves with the response.
|
* resolves with the response.
|
||||||
* @param {number} [args.times] - The number of times that the request is
|
* @param args.times - The number of times that the request is expected to be
|
||||||
* expected to be made.
|
* made.
|
||||||
* @returns {NockScope} The nock scope.
|
* @returns The nock scope.
|
||||||
*/
|
*/
|
||||||
function mockRpcCall({ nockScope, request, response, error, delay, times }) {
|
function mockRpcCall({
|
||||||
|
nockScope,
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
error,
|
||||||
|
delay,
|
||||||
|
times,
|
||||||
|
}: MockRpcCallOptions): MockRpcCallResult {
|
||||||
// eth-query always passes `params`, so even if we don't supply this property,
|
// eth-query always passes `params`, so even if we don't supply this property,
|
||||||
// for consistency with makeRpcCall, assume that the `body` contains it
|
// for consistency with makeRpcCall, assume that the `body` contains it
|
||||||
const { method, params = [], ...rest } = request;
|
const { method, params = [], ...rest } = request;
|
||||||
let httpStatus = 200;
|
let httpStatus = 200;
|
||||||
let completeResponse = { id: 2, jsonrpc: '2.0' };
|
let completeResponse: JSONRPCResponse = { id: 2, jsonrpc: '2.0' };
|
||||||
if (response !== undefined) {
|
if (response !== undefined) {
|
||||||
if ('body' in response) {
|
if ('body' in response) {
|
||||||
completeResponse = response.body;
|
completeResponse = response.body;
|
||||||
@ -156,6 +148,7 @@ function mockRpcCall({ nockScope, request, response, error, delay, times }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* @ts-expect-error The types for Nock do not include `basePath` in the interface for Nock.Scope. */
|
||||||
const url = nockScope.basePath.includes('infura.io')
|
const url = nockScope.basePath.includes('infura.io')
|
||||||
? `/v3/${MOCK_INFURA_PROJECT_ID}`
|
? `/v3/${MOCK_INFURA_PROJECT_ID}`
|
||||||
: '/';
|
: '/';
|
||||||
@ -189,7 +182,7 @@ function mockRpcCall({ nockScope, request, response, error, delay, times }) {
|
|||||||
if (error !== undefined) {
|
if (error !== undefined) {
|
||||||
return nockRequest.replyWithError(error);
|
return nockRequest.replyWithError(error);
|
||||||
} else if (completeResponse !== undefined) {
|
} else if (completeResponse !== undefined) {
|
||||||
return nockRequest.reply(httpStatus, (_, requestBody) => {
|
return nockRequest.reply(httpStatus, (_, requestBody: any) => {
|
||||||
if (response !== undefined && !('body' in response)) {
|
if (response !== undefined && !('body' in response)) {
|
||||||
if (response.id === undefined) {
|
if (response.id === undefined) {
|
||||||
completeResponse.id = requestBody.id;
|
completeResponse.id = requestBody.id;
|
||||||
@ -204,16 +197,72 @@ function mockRpcCall({ nockScope, request, response, error, delay, times }) {
|
|||||||
return nockRequest;
|
return nockRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MockBlockTrackerRequestOptions = {
|
||||||
|
/**
|
||||||
|
* A nock scope (a set of mocked requests scoped to a certain base url).
|
||||||
|
*/
|
||||||
|
nockScope: NockScope;
|
||||||
|
/**
|
||||||
|
* The block number that the block tracker should report, as a 0x-prefixed hex
|
||||||
|
* string.
|
||||||
|
*/
|
||||||
|
blockNumber: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocks the next request for the latest block that the block tracker will make.
|
||||||
|
*
|
||||||
|
* @param args - The arguments.
|
||||||
|
* @param args.nockScope - A nock scope (a set of mocked requests scoped to a
|
||||||
|
* certain base URL).
|
||||||
|
* @param args.blockNumber - The block number that the block tracker should
|
||||||
|
* report, as a 0x-prefixed hex string.
|
||||||
|
*/
|
||||||
|
function mockNextBlockTrackerRequest({
|
||||||
|
nockScope,
|
||||||
|
blockNumber = DEFAULT_LATEST_BLOCK_NUMBER,
|
||||||
|
}: MockBlockTrackerRequestOptions) {
|
||||||
|
mockRpcCall({
|
||||||
|
nockScope,
|
||||||
|
request: { method: 'eth_blockNumber', params: [] },
|
||||||
|
response: { result: blockNumber },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocks all requests for the latest block that the block tracker will make.
|
||||||
|
*
|
||||||
|
* @param args - The arguments.
|
||||||
|
* @param args.nockScope - A nock scope (a set of mocked requests scoped to a
|
||||||
|
* certain base URL).
|
||||||
|
* @param args.blockNumber - The block number that the block tracker should
|
||||||
|
* report, as a 0x-prefixed hex string.
|
||||||
|
*/
|
||||||
|
async function mockAllBlockTrackerRequests({
|
||||||
|
nockScope,
|
||||||
|
blockNumber = DEFAULT_LATEST_BLOCK_NUMBER,
|
||||||
|
}: MockBlockTrackerRequestOptions) {
|
||||||
|
const result = await mockRpcCall({
|
||||||
|
nockScope,
|
||||||
|
request: { method: 'eth_blockNumber', params: [] },
|
||||||
|
response: { result: blockNumber },
|
||||||
|
});
|
||||||
|
|
||||||
|
if ('persist' in result) {
|
||||||
|
result.persist();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a JSON-RPC call through the given eth-query object.
|
* Makes a JSON-RPC call through the given eth-query object.
|
||||||
*
|
*
|
||||||
* @param {any} ethQuery - The eth-query object.
|
* @param ethQuery - The eth-query object.
|
||||||
* @param {object} request - The request data.
|
* @param request - The request data.
|
||||||
* @returns {Promise<any>} A promise that either resolves with the result from
|
* @returns A promise that either resolves with the result from the JSON-RPC
|
||||||
* the JSON-RPC response if it is successful or rejects with the error from the
|
* response if it is successful or rejects with the error from the JSON-RPC
|
||||||
* JSON-RPC response otherwise.
|
* response otherwise.
|
||||||
*/
|
*/
|
||||||
function makeRpcCall(ethQuery, request) {
|
function makeRpcCall(ethQuery: EthQuery, request: Request) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
debug('[makeRpcCall] making request', request);
|
debug('[makeRpcCall] making request', request);
|
||||||
ethQuery.sendAsync(request, (error, result) => {
|
ethQuery.sendAsync(request, (error, result) => {
|
||||||
@ -227,41 +276,43 @@ function makeRpcCall(ethQuery, request) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export type ProviderType = 'infura' | 'custom';
|
||||||
* @typedef {{providerType: 'infura' | 'custom', infuraNetwork?: string}} WithMockedCommunicationsOptions
|
|
||||||
*
|
|
||||||
* The options bag that `Communications` takes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
export type MockOptions = {
|
||||||
* @typedef {{mockNextBlockTrackerRequest: (options: Omit<MockBlockTrackerRequestOptions, 'nockScope'>) => void, mockAllBlockTrackerRequests: (options: Omit<MockBlockTrackerRequestOptions, 'nockScope'>) => void, mockRpcCall: (options: Omit<MockRpcCallOptions, 'nockScope'>) => NockScope, rpcUrl: string, infuraNetwork: string}} Communications
|
infuraNetwork?: BuiltInInfuraNetwork;
|
||||||
*
|
providerType: ProviderType;
|
||||||
* Provides methods to mock different kinds of requests to the provider.
|
customRpcUrl?: string;
|
||||||
*/
|
customChainId?: Hex;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
export type MockCommunications = {
|
||||||
* @typedef {(comms: Communications) => Promise<any>} WithMockedCommunicationsCallback
|
mockNextBlockTrackerRequest: (options?: any) => void;
|
||||||
*
|
mockAllBlockTrackerRequests: (options?: any) => void;
|
||||||
* The callback that `mockingCommunications` takes.
|
mockRpcCall: (options: CurriedMockRpcCallOptions) => MockRpcCallResult;
|
||||||
*/
|
rpcUrl: string;
|
||||||
|
infuraNetwork: BuiltInInfuraNetwork;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up request mocks for requests to the provider.
|
* Sets up request mocks for requests to the provider.
|
||||||
*
|
*
|
||||||
* @param {WithMockedCommunicationsOptions} options - An options bag.
|
* @param options - An options bag.
|
||||||
* @param {"infura" | "custom"} options.providerType - The type of network
|
* @param options.providerType - The type of network client being tested.
|
||||||
* client being tested.
|
* @param options.infuraNetwork - The name of the Infura network being tested,
|
||||||
* @param {string} [options.infuraNetwork] - The name of the Infura network being
|
* assuming that `providerType` is "infura" (default: "mainnet").
|
||||||
* tested, assuming that `providerType` is "infura" (default: "mainnet").
|
* @param options.customRpcUrl - The URL of the custom RPC endpoint, assuming
|
||||||
* @param {string} [options.customRpcUrl] - The URL of the custom RPC endpoint,
|
* that `providerType` is "custom".
|
||||||
* assuming that `providerType` is "custom".
|
* @param fn - A function which will be called with an object that allows
|
||||||
* @param {WithMockedCommunicationsCallback} fn - A function which will be
|
* interaction with the network client.
|
||||||
* called with an object that allows interaction with the network client.
|
* @returns The return value of the given function.
|
||||||
* @returns {Promise<any>} The return value of the given function.
|
|
||||||
*/
|
*/
|
||||||
export async function withMockedCommunications(
|
export async function withMockedCommunications(
|
||||||
{ providerType, infuraNetwork = 'mainnet', customRpcUrl = MOCK_RPC_URL },
|
{
|
||||||
fn,
|
providerType,
|
||||||
|
infuraNetwork = 'mainnet',
|
||||||
|
customRpcUrl = MOCK_RPC_URL,
|
||||||
|
}: MockOptions,
|
||||||
|
fn: (comms: MockCommunications) => Promise<void>,
|
||||||
) {
|
) {
|
||||||
if (providerType !== 'infura' && providerType !== 'custom') {
|
if (providerType !== 'infura' && providerType !== 'custom') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -274,11 +325,11 @@ export async function withMockedCommunications(
|
|||||||
? `https://${infuraNetwork}.infura.io`
|
? `https://${infuraNetwork}.infura.io`
|
||||||
: customRpcUrl;
|
: customRpcUrl;
|
||||||
const nockScope = buildScopeForMockingRequests(rpcUrl);
|
const nockScope = buildScopeForMockingRequests(rpcUrl);
|
||||||
const curriedMockNextBlockTrackerRequest = (localOptions) =>
|
const curriedMockNextBlockTrackerRequest = (localOptions: any) =>
|
||||||
mockNextBlockTrackerRequest({ nockScope, ...localOptions });
|
mockNextBlockTrackerRequest({ nockScope, ...localOptions });
|
||||||
const curriedMockAllBlockTrackerRequests = (localOptions) =>
|
const curriedMockAllBlockTrackerRequests = (localOptions: any) =>
|
||||||
mockAllBlockTrackerRequests({ nockScope, ...localOptions });
|
mockAllBlockTrackerRequests({ nockScope, ...localOptions });
|
||||||
const curriedMockRpcCall = (localOptions) =>
|
const curriedMockRpcCall = (localOptions: any) =>
|
||||||
mockRpcCall({ nockScope, ...localOptions });
|
mockRpcCall({ nockScope, ...localOptions });
|
||||||
|
|
||||||
const comms = {
|
const comms = {
|
||||||
@ -297,12 +348,12 @@ export async function withMockedCommunications(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
type MockNetworkClient = {
|
||||||
* @typedef {{blockTracker: import('eth-block-tracker').PollingBlockTracker, clock: sinon.SinonFakeTimers, makeRpcCall: (request: Partial<JsonRpcRequest>) => Promise<any>, makeRpcCallsInSeries: (requests: Partial<JsonRpcRequest>[]) => Promise<any>}} MockNetworkClient
|
blockTracker: any;
|
||||||
*
|
clock: sinon.SinonFakeTimers;
|
||||||
* Provides methods to interact with the suite of middleware that
|
makeRpcCall: (request: Request) => Promise<any>;
|
||||||
* `createInfuraClient` or `createJsonRpcClient` exposes.
|
makeRpcCallsInSeries: (requests: Request[]) => Promise<any[]>;
|
||||||
*/
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some middleware contain logic which retries the request if some condition
|
* Some middleware contain logic which retries the request if some condition
|
||||||
@ -321,14 +372,14 @@ export async function withMockedCommunications(
|
|||||||
* `setTimeout` handler.
|
* `setTimeout` handler.
|
||||||
*/
|
*/
|
||||||
export async function waitForPromiseToBeFulfilledAfterRunningAllTimers(
|
export async function waitForPromiseToBeFulfilledAfterRunningAllTimers(
|
||||||
promise,
|
promise: any,
|
||||||
clock,
|
clock: any,
|
||||||
) {
|
) {
|
||||||
let hasPromiseBeenFulfilled = false;
|
let hasPromiseBeenFulfilled = false;
|
||||||
let numTimesClockHasBeenAdvanced = 0;
|
let numTimesClockHasBeenAdvanced = 0;
|
||||||
|
|
||||||
promise
|
promise
|
||||||
.catch((error) => {
|
.catch((error: any) => {
|
||||||
// This is used to silence Node.js warnings about the rejection
|
// This is used to silence Node.js warnings about the rejection
|
||||||
// being handled asynchronously. The error is handled later when
|
// being handled asynchronously. The error is handled later when
|
||||||
// `promise` is awaited, but we log it here anyway in case it gets
|
// `promise` is awaited, but we log it here anyway in case it gets
|
||||||
@ -350,36 +401,22 @@ export async function waitForPromiseToBeFulfilledAfterRunningAllTimers(
|
|||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {{providerType: "infura" | "custom", infuraNetwork?: string, customRpcUrl?: string, customChainId?: string}} WithClientOptions
|
|
||||||
*
|
|
||||||
* The options bag that `withNetworkClient` takes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {(client: MockNetworkClient) => Promise<any>} WithClientCallback
|
|
||||||
*
|
|
||||||
* The callback that `withNetworkClient` takes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a provider from the middleware (for the provider type) along with a
|
* Builds a provider from the middleware (for the provider type) along with a
|
||||||
* block tracker, runs the given function with those two things, and then
|
* block tracker, runs the given function with those two things, and then
|
||||||
* ensures the block tracker is stopped at the end.
|
* ensures the block tracker is stopped at the end.
|
||||||
*
|
*
|
||||||
* @param {WithClientOptions} options - An options bag.
|
* @param options - An options bag.
|
||||||
* @param {"infura" | "custom"} options.providerType - The type of network
|
* @param options.providerType - The type of network client being tested.
|
||||||
* client being tested.
|
* @param options.infuraNetwork - The name of the Infura network being tested,
|
||||||
* @param {string} [options.infuraNetwork] - The name of the Infura network being
|
* assuming that `providerType` is "infura" (default: "mainnet").
|
||||||
* tested, assuming that `providerType` is "infura" (default: "mainnet").
|
* @param options.customRpcUrl - The URL of the custom RPC endpoint, assuming
|
||||||
* @param {string} [options.customRpcUrl] - The URL of the custom RPC endpoint,
|
* that `providerType` is "custom".
|
||||||
* assuming that `providerType` is "custom".
|
* @param options.customChainId - The chain id belonging to the custom RPC
|
||||||
* @param {string} [options.customChainId] - The chain id belonging to the
|
* endpoint, assuming that `providerType` is "custom" (default: "0x1").
|
||||||
* custom RPC endpoint, assuming that `providerType` is "custom" (default:
|
* @param fn - A function which will be called with an object that allows
|
||||||
* "0x1").
|
* interaction with the network client.
|
||||||
* @param {WithClientCallback} fn - A function which will be called with an
|
* @returns The return value of the given function.
|
||||||
* object that allows interaction with the network client.
|
|
||||||
* @returns {Promise<any>} The return value of the given function.
|
|
||||||
*/
|
*/
|
||||||
export async function withNetworkClient(
|
export async function withNetworkClient(
|
||||||
{
|
{
|
||||||
@ -387,8 +424,8 @@ export async function withNetworkClient(
|
|||||||
infuraNetwork = 'mainnet',
|
infuraNetwork = 'mainnet',
|
||||||
customRpcUrl = MOCK_RPC_URL,
|
customRpcUrl = MOCK_RPC_URL,
|
||||||
customChainId = '0x1',
|
customChainId = '0x1',
|
||||||
},
|
}: MockOptions,
|
||||||
fn,
|
fn: (client: MockNetworkClient) => Promise<any>,
|
||||||
) {
|
) {
|
||||||
if (providerType !== 'infura' && providerType !== 'custom') {
|
if (providerType !== 'infura' && providerType !== 'custom') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -414,20 +451,21 @@ export async function withNetworkClient(
|
|||||||
? createNetworkClient({
|
? createNetworkClient({
|
||||||
network: infuraNetwork,
|
network: infuraNetwork,
|
||||||
infuraProjectId: MOCK_INFURA_PROJECT_ID,
|
infuraProjectId: MOCK_INFURA_PROJECT_ID,
|
||||||
type: 'infura',
|
type: NetworkClientType.Infura,
|
||||||
})
|
})
|
||||||
: createNetworkClient({
|
: createNetworkClient({
|
||||||
chainId: customChainId,
|
chainId: customChainId,
|
||||||
rpcUrl: customRpcUrl,
|
rpcUrl: customRpcUrl,
|
||||||
type: 'custom',
|
type: NetworkClientType.Custom,
|
||||||
});
|
});
|
||||||
process.env.IN_TEST = inTest;
|
process.env.IN_TEST = inTest;
|
||||||
|
|
||||||
const { provider, blockTracker } = clientUnderTest;
|
const { provider, blockTracker } = clientUnderTest;
|
||||||
|
|
||||||
const ethQuery = new EthQuery(provider);
|
const ethQuery = new EthQuery(provider);
|
||||||
const curriedMakeRpcCall = (request) => makeRpcCall(ethQuery, request);
|
const curriedMakeRpcCall = (request: Request) =>
|
||||||
const makeRpcCallsInSeries = async (requests) => {
|
makeRpcCall(ethQuery, request);
|
||||||
|
const makeRpcCallsInSeries = async (requests: Request[]) => {
|
||||||
const responses = [];
|
const responses = [];
|
||||||
for (const request of requests) {
|
for (const request of requests) {
|
||||||
responses.push(await curriedMakeRpcCall(request));
|
responses.push(await curriedMakeRpcCall(request));
|
||||||
@ -451,6 +489,13 @@ export async function withNetworkClient(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BuildMockParamsOptions = {
|
||||||
|
// The block parameter value to set.
|
||||||
|
blockParam: any;
|
||||||
|
// The index of the block parameter.
|
||||||
|
blockParamIndex: number;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build mock parameters for a JSON-RPC call.
|
* Build mock parameters for a JSON-RPC call.
|
||||||
*
|
*
|
||||||
@ -460,16 +505,15 @@ export async function withNetworkClient(
|
|||||||
* The block parameter can be set to a custom value. If no value is given, it
|
* The block parameter can be set to a custom value. If no value is given, it
|
||||||
* is set as undefined.
|
* is set as undefined.
|
||||||
*
|
*
|
||||||
* @param {object} args - Arguments.
|
* @param args - Arguments.
|
||||||
* @param {number} args.blockParamIndex - The index of the block parameter.
|
* @param args.blockParamIndex - The index of the block parameter.
|
||||||
* @param {any} [args.blockParam] - The block parameter value to set.
|
* @param args.blockParam - The block parameter value to set.
|
||||||
* @returns {any[]} The mock params.
|
* @returns The mock params.
|
||||||
*/
|
*/
|
||||||
export function buildMockParams({ blockParam, blockParamIndex }) {
|
export function buildMockParams({
|
||||||
if (blockParamIndex === undefined) {
|
blockParam,
|
||||||
throw new Error(`Missing 'blockParamIndex'`);
|
blockParamIndex,
|
||||||
}
|
}: BuildMockParamsOptions) {
|
||||||
|
|
||||||
const params = new Array(blockParamIndex).fill('some value');
|
const params = new Array(blockParamIndex).fill('some value');
|
||||||
params[blockParamIndex] = blockParam;
|
params[blockParamIndex] = blockParam;
|
||||||
|
|
||||||
@ -480,18 +524,18 @@ export function buildMockParams({ blockParam, blockParamIndex }) {
|
|||||||
* Returns a partial JSON-RPC request object, with the "block" param replaced
|
* Returns a partial JSON-RPC request object, with the "block" param replaced
|
||||||
* with the given value.
|
* with the given value.
|
||||||
*
|
*
|
||||||
* @param {object} request - The request object.
|
* @param request - The request object.
|
||||||
* @param {string} request.method - The request method.
|
* @param request.method - The request method.
|
||||||
* @param {params} [request.params] - The request params.
|
* @param request.params - The request params.
|
||||||
* @param {number} blockParamIndex - The index within the `params` array of the
|
* @param blockParamIndex - The index within the `params` array of the block
|
||||||
* block param.
|
* param.
|
||||||
* @param {any} blockParam - The desired block param value.
|
* @param blockParam - The desired block param value.
|
||||||
* @returns {object} The updated request object.
|
* @returns The updated request object.
|
||||||
*/
|
*/
|
||||||
export function buildRequestWithReplacedBlockParam(
|
export function buildRequestWithReplacedBlockParam(
|
||||||
{ method, params = [] },
|
{ method, params = [] }: Request,
|
||||||
blockParamIndex,
|
blockParamIndex: number,
|
||||||
blockParam,
|
blockParam: any,
|
||||||
) {
|
) {
|
||||||
const updatedParams = params.slice();
|
const updatedParams = params.slice();
|
||||||
updatedParams[blockParamIndex] = blockParam;
|
updatedParams[blockParamIndex] = blockParam;
|
@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable jest/require-top-level-describe, jest/no-export */
|
/* eslint-disable jest/require-top-level-describe, jest/no-export */
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ProviderType,
|
||||||
waitForPromiseToBeFulfilledAfterRunningAllTimers,
|
waitForPromiseToBeFulfilledAfterRunningAllTimers,
|
||||||
withMockedCommunications,
|
withMockedCommunications,
|
||||||
withNetworkClient,
|
withNetworkClient,
|
||||||
@ -11,6 +12,11 @@ import {
|
|||||||
buildJsonRpcEngineEmptyResponseErrorMessage,
|
buildJsonRpcEngineEmptyResponseErrorMessage,
|
||||||
} from './shared-tests';
|
} from './shared-tests';
|
||||||
|
|
||||||
|
type TestsForRpcMethodAssumingNoBlockParamOptions = {
|
||||||
|
providerType: ProviderType;
|
||||||
|
numberOfParameters: number;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines tests which exercise the behavior exhibited by an RPC method which is
|
* Defines tests which exercise the behavior exhibited by an RPC method which is
|
||||||
* assumed to not take a block parameter. Even if it does, the value of this
|
* assumed to not take a block parameter. Even if it does, the value of this
|
||||||
@ -23,8 +29,11 @@ import {
|
|||||||
* either `infura` or `custom` (default: "infura").
|
* either `infura` or `custom` (default: "infura").
|
||||||
*/
|
*/
|
||||||
export function testsForRpcMethodAssumingNoBlockParam(
|
export function testsForRpcMethodAssumingNoBlockParam(
|
||||||
method,
|
method: string,
|
||||||
{ numberOfParameters, providerType },
|
{
|
||||||
|
numberOfParameters,
|
||||||
|
providerType,
|
||||||
|
}: TestsForRpcMethodAssumingNoBlockParamOptions,
|
||||||
) {
|
) {
|
||||||
if (providerType !== 'infura' && providerType !== 'custom') {
|
if (providerType !== 'infura' && providerType !== 'custom') {
|
||||||
throw new Error(
|
throw new Error(
|
@ -1,7 +1,16 @@
|
|||||||
/* eslint-disable jest/require-top-level-describe, jest/no-export */
|
/* eslint-disable jest/require-top-level-describe, jest/no-export */
|
||||||
|
|
||||||
import { fill } from 'lodash';
|
import { fill } from 'lodash';
|
||||||
import { withMockedCommunications, withNetworkClient } from './helpers';
|
import {
|
||||||
|
ProviderType,
|
||||||
|
withMockedCommunications,
|
||||||
|
withNetworkClient,
|
||||||
|
} from './helpers';
|
||||||
|
|
||||||
|
type TestsForRpcMethodNotHandledByMiddlewareOptions = {
|
||||||
|
providerType: ProviderType;
|
||||||
|
numberOfParameters: number;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines tests which exercise the behavior exhibited by an RPC method that
|
* Defines tests which exercise the behavior exhibited by an RPC method that
|
||||||
@ -15,8 +24,11 @@ import { withMockedCommunications, withNetworkClient } from './helpers';
|
|||||||
* RPC method takes.
|
* RPC method takes.
|
||||||
*/
|
*/
|
||||||
export function testsForRpcMethodNotHandledByMiddleware(
|
export function testsForRpcMethodNotHandledByMiddleware(
|
||||||
method,
|
method: string,
|
||||||
{ providerType, numberOfParameters },
|
{
|
||||||
|
providerType,
|
||||||
|
numberOfParameters,
|
||||||
|
}: TestsForRpcMethodNotHandledByMiddlewareOptions,
|
||||||
) {
|
) {
|
||||||
if (providerType !== 'infura' && providerType !== 'custom') {
|
if (providerType !== 'infura' && providerType !== 'custom') {
|
||||||
throw new Error(
|
throw new Error(
|
@ -2,7 +2,11 @@
|
|||||||
|
|
||||||
import { testsForRpcMethodsThatCheckForBlockHashInResponse } from './block-hash-in-response';
|
import { testsForRpcMethodsThatCheckForBlockHashInResponse } from './block-hash-in-response';
|
||||||
import { testsForRpcMethodSupportingBlockParam } from './block-param';
|
import { testsForRpcMethodSupportingBlockParam } from './block-param';
|
||||||
import { withMockedCommunications, withNetworkClient } from './helpers';
|
import {
|
||||||
|
ProviderType,
|
||||||
|
withMockedCommunications,
|
||||||
|
withNetworkClient,
|
||||||
|
} from './helpers';
|
||||||
import { testsForRpcMethodAssumingNoBlockParam } from './no-block-param';
|
import { testsForRpcMethodAssumingNoBlockParam } from './no-block-param';
|
||||||
import { testsForRpcMethodNotHandledByMiddleware } from './not-handled-by-middleware';
|
import { testsForRpcMethodNotHandledByMiddleware } from './not-handled-by-middleware';
|
||||||
|
|
||||||
@ -13,7 +17,7 @@ import { testsForRpcMethodNotHandledByMiddleware } from './not-handled-by-middle
|
|||||||
* @param reason - The exact reason for failure.
|
* @param reason - The exact reason for failure.
|
||||||
* @returns The error message.
|
* @returns The error message.
|
||||||
*/
|
*/
|
||||||
export function buildInfuraClientRetriesExhaustedErrorMessage(reason) {
|
export function buildInfuraClientRetriesExhaustedErrorMessage(reason: string) {
|
||||||
return new RegExp(
|
return new RegExp(
|
||||||
`^InfuraProvider - cannot complete request. All retries exhausted\\..+${reason}`,
|
`^InfuraProvider - cannot complete request. All retries exhausted\\..+${reason}`,
|
||||||
'us',
|
'us',
|
||||||
@ -27,7 +31,7 @@ export function buildInfuraClientRetriesExhaustedErrorMessage(reason) {
|
|||||||
* @param method - The RPC method.
|
* @param method - The RPC method.
|
||||||
* @returns The error message.
|
* @returns The error message.
|
||||||
*/
|
*/
|
||||||
export function buildJsonRpcEngineEmptyResponseErrorMessage(method) {
|
export function buildJsonRpcEngineEmptyResponseErrorMessage(method: string) {
|
||||||
return new RegExp(
|
return new RegExp(
|
||||||
`^JsonRpcEngine: Response has no error or result for request:.+"method": "${method}"`,
|
`^JsonRpcEngine: Response has no error or result for request:.+"method": "${method}"`,
|
||||||
'us',
|
'us',
|
||||||
@ -42,7 +46,7 @@ export function buildJsonRpcEngineEmptyResponseErrorMessage(method) {
|
|||||||
* @param reason - The reason.
|
* @param reason - The reason.
|
||||||
* @returns The error message.
|
* @returns The error message.
|
||||||
*/
|
*/
|
||||||
export function buildFetchFailedErrorMessage(url, reason) {
|
export function buildFetchFailedErrorMessage(url: string, reason: string) {
|
||||||
return new RegExp(
|
return new RegExp(
|
||||||
`^request to ${url}(/[^/ ]*)+ failed, reason: ${reason}`,
|
`^request to ${url}(/[^/ ]*)+ failed, reason: ${reason}`,
|
||||||
'us',
|
'us',
|
||||||
@ -57,7 +61,7 @@ export function buildFetchFailedErrorMessage(url, reason) {
|
|||||||
* exposed by `createInfuraClient` is tested; if `custom`, then the middleware
|
* exposed by `createInfuraClient` is tested; if `custom`, then the middleware
|
||||||
* exposed by `createJsonRpcClient` will be tested.
|
* exposed by `createJsonRpcClient` will be tested.
|
||||||
*/
|
*/
|
||||||
export function testsForProviderType(providerType) {
|
export function testsForProviderType(providerType: ProviderType) {
|
||||||
// Ethereum JSON-RPC spec: <https://ethereum.github.io/execution-apis/api-documentation/>
|
// Ethereum JSON-RPC spec: <https://ethereum.github.io/execution-apis/api-documentation/>
|
||||||
// Infura documentation: <https://docs.infura.io/infura/networks/ethereum/json-rpc-methods>
|
// Infura documentation: <https://docs.infura.io/infura/networks/ethereum/json-rpc-methods>
|
||||||
|
|
@ -4,7 +4,7 @@ import { ControllerMessenger } from '@metamask/base-controller';
|
|||||||
import { TokenListController } from '@metamask/assets-controllers';
|
import { TokenListController } from '@metamask/assets-controllers';
|
||||||
import { CHAIN_IDS } from '../../../shared/constants/network';
|
import { CHAIN_IDS } from '../../../shared/constants/network';
|
||||||
import PreferencesController from './preferences';
|
import PreferencesController from './preferences';
|
||||||
import NetworkController from './network';
|
import { NetworkController } from './network';
|
||||||
|
|
||||||
describe('preferences controller', function () {
|
describe('preferences controller', function () {
|
||||||
let preferencesController;
|
let preferencesController;
|
||||||
|
@ -409,6 +409,14 @@ describe('SignController', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not throw if accepting approval throws', async () => {
|
||||||
|
messengerMock.call.mockImplementation(() => {
|
||||||
|
throw new Error('Test Error');
|
||||||
|
});
|
||||||
|
|
||||||
|
await signController[signMethodName](messageParamsMock);
|
||||||
|
});
|
||||||
|
|
||||||
it('rejects message on error', async () => {
|
it('rejects message on error', async () => {
|
||||||
keyringControllerMock[signMethodName].mockReset();
|
keyringControllerMock[signMethodName].mockReset();
|
||||||
keyringControllerMock[signMethodName].mockRejectedValue(
|
keyringControllerMock[signMethodName].mockRejectedValue(
|
||||||
@ -468,6 +476,14 @@ describe('SignController', () => {
|
|||||||
'Cancel',
|
'Cancel',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not throw if rejecting approval throws', async () => {
|
||||||
|
messengerMock.call.mockImplementation(() => {
|
||||||
|
throw new Error('Test Error');
|
||||||
|
});
|
||||||
|
|
||||||
|
await signController[cancelMethodName](messageParamsMock);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('message manager events', () => {
|
describe('message manager events', () => {
|
||||||
|
@ -33,12 +33,13 @@ import {
|
|||||||
RejectRequest,
|
RejectRequest,
|
||||||
} from '@metamask/approval-controller';
|
} from '@metamask/approval-controller';
|
||||||
import { MetaMetricsEventCategory } from '../../../shared/constants/metametrics';
|
import { MetaMetricsEventCategory } from '../../../shared/constants/metametrics';
|
||||||
|
import { MESSAGE_TYPE } from '../../../shared/constants/app';
|
||||||
import PreferencesController from './preferences';
|
import PreferencesController from './preferences';
|
||||||
|
|
||||||
const controllerName = 'SignController';
|
const controllerName = 'SignController';
|
||||||
const methodNameSign = 'eth_sign';
|
const methodNameSign = MESSAGE_TYPE.ETH_SIGN;
|
||||||
const methodNamePersonalSign = 'personal_sign';
|
const methodNamePersonalSign = MESSAGE_TYPE.PERSONAL_SIGN;
|
||||||
const methodNameTypedSign = 'eth_signTypedData';
|
const methodNameTypedSign = MESSAGE_TYPE.ETH_SIGN_TYPED_DATA;
|
||||||
|
|
||||||
const stateMetadata = {
|
const stateMetadata = {
|
||||||
unapprovedMsgs: { persist: false, anonymous: false },
|
unapprovedMsgs: { persist: false, anonymous: false },
|
||||||
@ -104,7 +105,6 @@ export type SignControllerOptions = {
|
|||||||
messenger: SignControllerMessenger;
|
messenger: SignControllerMessenger;
|
||||||
keyringController: KeyringController;
|
keyringController: KeyringController;
|
||||||
preferencesController: PreferencesController;
|
preferencesController: PreferencesController;
|
||||||
sendUpdate: () => void;
|
|
||||||
getState: () => any;
|
getState: () => any;
|
||||||
metricsEvent: (payload: any, options?: any) => void;
|
metricsEvent: (payload: any, options?: any) => void;
|
||||||
securityProviderRequest: (
|
securityProviderRequest: (
|
||||||
@ -637,14 +637,22 @@ export default class SignController extends BaseControllerV2<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _acceptApproval(messageId: string) {
|
private _acceptApproval(messageId: string) {
|
||||||
|
try {
|
||||||
this.messagingSystem.call('ApprovalController:acceptRequest', messageId);
|
this.messagingSystem.call('ApprovalController:acceptRequest', messageId);
|
||||||
|
} catch (error) {
|
||||||
|
log.info('Failed to accept signature approval request', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _rejectApproval(messageId: string) {
|
private _rejectApproval(messageId: string) {
|
||||||
|
try {
|
||||||
this.messagingSystem.call(
|
this.messagingSystem.call(
|
||||||
'ApprovalController:rejectRequest',
|
'ApprovalController:rejectRequest',
|
||||||
messageId,
|
messageId,
|
||||||
'Cancel',
|
'Cancel',
|
||||||
);
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.info('Failed to reject signature approval request', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,10 @@ import {
|
|||||||
determineTransactionType,
|
determineTransactionType,
|
||||||
isEIP1559Transaction,
|
isEIP1559Transaction,
|
||||||
} from '../../../../shared/modules/transaction.utils';
|
} from '../../../../shared/modules/transaction.utils';
|
||||||
import { ORIGIN_METAMASK } from '../../../../shared/constants/app';
|
import {
|
||||||
|
ORIGIN_METAMASK,
|
||||||
|
MESSAGE_TYPE,
|
||||||
|
} from '../../../../shared/constants/app';
|
||||||
import {
|
import {
|
||||||
calcGasTotal,
|
calcGasTotal,
|
||||||
getSwapsTokensReceivedFromTxMeta,
|
getSwapsTokensReceivedFromTxMeta,
|
||||||
@ -156,6 +159,7 @@ export default class TransactionController extends EventEmitter {
|
|||||||
this.getAccountType = opts.getAccountType;
|
this.getAccountType = opts.getAccountType;
|
||||||
this.getTokenStandardAndDetails = opts.getTokenStandardAndDetails;
|
this.getTokenStandardAndDetails = opts.getTokenStandardAndDetails;
|
||||||
this.securityProviderRequest = opts.securityProviderRequest;
|
this.securityProviderRequest = opts.securityProviderRequest;
|
||||||
|
this.messagingSystem = opts.messenger;
|
||||||
|
|
||||||
this.memStore = new ObservableStore({});
|
this.memStore = new ObservableStore({});
|
||||||
|
|
||||||
@ -798,6 +802,7 @@ export default class TransactionController extends EventEmitter {
|
|||||||
this.txStateManager.getTransactionWithActionId(actionId);
|
this.txStateManager.getTransactionWithActionId(actionId);
|
||||||
if (existingTxMeta) {
|
if (existingTxMeta) {
|
||||||
this.emit('newUnapprovedTx', existingTxMeta);
|
this.emit('newUnapprovedTx', existingTxMeta);
|
||||||
|
this._requestApproval(existingTxMeta);
|
||||||
existingTxMeta = await this.addTransactionGasDefaults(existingTxMeta);
|
existingTxMeta = await this.addTransactionGasDefaults(existingTxMeta);
|
||||||
return existingTxMeta;
|
return existingTxMeta;
|
||||||
}
|
}
|
||||||
@ -870,6 +875,7 @@ export default class TransactionController extends EventEmitter {
|
|||||||
|
|
||||||
this.addTransaction(txMeta);
|
this.addTransaction(txMeta);
|
||||||
this.emit('newUnapprovedTx', txMeta);
|
this.emit('newUnapprovedTx', txMeta);
|
||||||
|
this._requestApproval(txMeta);
|
||||||
|
|
||||||
txMeta = await this.addTransactionGasDefaults(txMeta);
|
txMeta = await this.addTransactionGasDefaults(txMeta);
|
||||||
|
|
||||||
@ -1355,6 +1361,7 @@ export default class TransactionController extends EventEmitter {
|
|||||||
try {
|
try {
|
||||||
// approve
|
// approve
|
||||||
this.txStateManager.setTxStatusApproved(txId);
|
this.txStateManager.setTxStatusApproved(txId);
|
||||||
|
this._acceptApproval(txMeta);
|
||||||
// get next nonce
|
// get next nonce
|
||||||
const fromAddress = txMeta.txParams.from;
|
const fromAddress = txMeta.txParams.from;
|
||||||
// wait for a nonce
|
// wait for a nonce
|
||||||
@ -1734,6 +1741,7 @@ export default class TransactionController extends EventEmitter {
|
|||||||
async cancelTransaction(txId, actionId) {
|
async cancelTransaction(txId, actionId) {
|
||||||
const txMeta = this.txStateManager.getTransaction(txId);
|
const txMeta = this.txStateManager.getTransaction(txId);
|
||||||
this.txStateManager.setTxStatusRejected(txId);
|
this.txStateManager.setTxStatusRejected(txId);
|
||||||
|
this._rejectApproval(txMeta);
|
||||||
this._trackTransactionMetricsEvent(
|
this._trackTransactionMetricsEvent(
|
||||||
txMeta,
|
txMeta,
|
||||||
TransactionMetaMetricsEvent.rejected,
|
TransactionMetaMetricsEvent.rejected,
|
||||||
@ -2596,4 +2604,54 @@ export default class TransactionController extends EventEmitter {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_requestApproval(txMeta) {
|
||||||
|
const id = this._getApprovalId(txMeta);
|
||||||
|
const { origin } = txMeta;
|
||||||
|
const type = MESSAGE_TYPE.TRANSACTION;
|
||||||
|
const requestData = { txId: txMeta.id };
|
||||||
|
|
||||||
|
this.messagingSystem
|
||||||
|
.call(
|
||||||
|
'ApprovalController:addRequest',
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
origin,
|
||||||
|
type,
|
||||||
|
requestData,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.catch(() => {
|
||||||
|
// Intentionally ignored as promise not currently used
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_acceptApproval(txMeta) {
|
||||||
|
const id = this._getApprovalId(txMeta);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.messagingSystem.call('ApprovalController:acceptRequest', id);
|
||||||
|
} catch (error) {
|
||||||
|
log.error('Failed to accept transaction approval request', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_rejectApproval(txMeta) {
|
||||||
|
const id = this._getApprovalId(txMeta);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.messagingSystem.call(
|
||||||
|
'ApprovalController:rejectRequest',
|
||||||
|
id,
|
||||||
|
new Error('Rejected'),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error('Failed to reject transaction approval request', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_getApprovalId(txMeta) {
|
||||||
|
return String(txMeta.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,10 @@ import {
|
|||||||
GasRecommendations,
|
GasRecommendations,
|
||||||
} from '../../../../shared/constants/gas';
|
} from '../../../../shared/constants/gas';
|
||||||
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
|
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
|
||||||
import { ORIGIN_METAMASK } from '../../../../shared/constants/app';
|
import {
|
||||||
|
MESSAGE_TYPE,
|
||||||
|
ORIGIN_METAMASK,
|
||||||
|
} from '../../../../shared/constants/app';
|
||||||
import { NetworkStatus } from '../../../../shared/constants/network';
|
import { NetworkStatus } from '../../../../shared/constants/network';
|
||||||
import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../shared/lib/transactions-controller-utils';
|
import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../shared/lib/transactions-controller-utils';
|
||||||
import TransactionController from '.';
|
import TransactionController from '.';
|
||||||
@ -52,7 +55,8 @@ describe('Transaction Controller', function () {
|
|||||||
fromAccount,
|
fromAccount,
|
||||||
fragmentExists,
|
fragmentExists,
|
||||||
networkStatusStore,
|
networkStatusStore,
|
||||||
getCurrentChainId;
|
getCurrentChainId,
|
||||||
|
messengerMock;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
fragmentExists = false;
|
fragmentExists = false;
|
||||||
@ -76,6 +80,7 @@ describe('Transaction Controller', function () {
|
|||||||
blockTrackerStub.getLatestBlock = noop;
|
blockTrackerStub.getLatestBlock = noop;
|
||||||
|
|
||||||
getCurrentChainId = sinon.stub().callsFake(() => currentChainId);
|
getCurrentChainId = sinon.stub().callsFake(() => currentChainId);
|
||||||
|
messengerMock = { call: sinon.stub().returns(Promise.resolve()) };
|
||||||
|
|
||||||
txController = new TransactionController({
|
txController = new TransactionController({
|
||||||
provider,
|
provider,
|
||||||
@ -108,6 +113,7 @@ describe('Transaction Controller', function () {
|
|||||||
getAccountType: () => 'MetaMask',
|
getAccountType: () => 'MetaMask',
|
||||||
getDeviceModel: () => 'N/A',
|
getDeviceModel: () => 'N/A',
|
||||||
securityProviderRequest: () => undefined,
|
securityProviderRequest: () => undefined,
|
||||||
|
messenger: messengerMock,
|
||||||
});
|
});
|
||||||
txController.nonceTracker.getNonceLock = () =>
|
txController.nonceTracker.getNonceLock = () =>
|
||||||
Promise.resolve({ nextNonce: 0, releaseLock: noop });
|
Promise.resolve({ nextNonce: 0, releaseLock: noop });
|
||||||
@ -489,6 +495,67 @@ describe('Transaction Controller', function () {
|
|||||||
{ message: 'MetaMask is having trouble connecting to the network' },
|
{ message: 'MetaMask is having trouble connecting to the network' },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should create an approval request', async function () {
|
||||||
|
const txMeta = await txController.addUnapprovedTransaction(
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
from: selectedAddress,
|
||||||
|
to: recipientAddress,
|
||||||
|
},
|
||||||
|
ORIGIN_METAMASK,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(messengerMock.call.callCount, 1);
|
||||||
|
assert.deepEqual(messengerMock.call.getCall(0).args, [
|
||||||
|
'ApprovalController:addRequest',
|
||||||
|
{
|
||||||
|
id: String(txMeta.id),
|
||||||
|
origin: ORIGIN_METAMASK,
|
||||||
|
requestData: { txId: txMeta.id },
|
||||||
|
type: MESSAGE_TYPE.TRANSACTION,
|
||||||
|
},
|
||||||
|
true, // Show popup
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should still create an approval request when called twice with same actionId', async function () {
|
||||||
|
await txController.addUnapprovedTransaction(
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
from: selectedAddress,
|
||||||
|
to: recipientAddress,
|
||||||
|
},
|
||||||
|
ORIGIN_METAMASK,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
'12345',
|
||||||
|
);
|
||||||
|
|
||||||
|
const secondTxMeta = await txController.addUnapprovedTransaction(
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
from: selectedAddress,
|
||||||
|
to: recipientAddress,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
'12345',
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(messengerMock.call.callCount, 2);
|
||||||
|
assert.deepEqual(messengerMock.call.getCall(1).args, [
|
||||||
|
'ApprovalController:addRequest',
|
||||||
|
{
|
||||||
|
id: String(secondTxMeta.id),
|
||||||
|
origin: ORIGIN_METAMASK,
|
||||||
|
requestData: { txId: secondTxMeta.id },
|
||||||
|
type: MESSAGE_TYPE.TRANSACTION,
|
||||||
|
},
|
||||||
|
true, // Show popup
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#createCancelTransaction', function () {
|
describe('#createCancelTransaction', function () {
|
||||||
@ -997,9 +1064,11 @@ describe('Transaction Controller', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('#approveTransaction', function () {
|
describe('#approveTransaction', function () {
|
||||||
it('does not overwrite set values', async function () {
|
let originalValue, txMeta, signStub, pubStub;
|
||||||
const originalValue = '0x01';
|
|
||||||
const txMeta = {
|
beforeEach(function () {
|
||||||
|
originalValue = '0x01';
|
||||||
|
txMeta = {
|
||||||
id: '1',
|
id: '1',
|
||||||
status: TransactionStatus.unapproved,
|
status: TransactionStatus.unapproved,
|
||||||
metamaskNetworkId: currentNetworkId,
|
metamaskNetworkId: currentNetworkId,
|
||||||
@ -1019,17 +1088,22 @@ describe('Transaction Controller', function () {
|
|||||||
providerResultStub.eth_gasPrice = wrongValue;
|
providerResultStub.eth_gasPrice = wrongValue;
|
||||||
providerResultStub.eth_estimateGas = '0x5209';
|
providerResultStub.eth_estimateGas = '0x5209';
|
||||||
|
|
||||||
const signStub = sinon
|
signStub = sinon
|
||||||
.stub(txController, 'signTransaction')
|
.stub(txController, 'signTransaction')
|
||||||
.callsFake(() => Promise.resolve());
|
.callsFake(() => Promise.resolve());
|
||||||
|
|
||||||
const pubStub = sinon
|
pubStub = sinon.stub(txController, 'publishTransaction').callsFake(() => {
|
||||||
.stub(txController, 'publishTransaction')
|
|
||||||
.callsFake(() => {
|
|
||||||
txController.setTxHash('1', originalValue);
|
txController.setTxHash('1', originalValue);
|
||||||
txController.txStateManager.setTxStatusSubmitted('1');
|
txController.txStateManager.setTxStatusSubmitted('1');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
signStub.restore();
|
||||||
|
pubStub.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not overwrite set values', async function () {
|
||||||
await txController.approveTransaction(txMeta.id);
|
await txController.approveTransaction(txMeta.id);
|
||||||
const result = txController.txStateManager.getTransaction(txMeta.id);
|
const result = txController.txStateManager.getTransaction(txMeta.id);
|
||||||
const params = result.txParams;
|
const params = result.txParams;
|
||||||
@ -1042,8 +1116,21 @@ describe('Transaction Controller', function () {
|
|||||||
TransactionStatus.submitted,
|
TransactionStatus.submitted,
|
||||||
'should have reached the submitted status.',
|
'should have reached the submitted status.',
|
||||||
);
|
);
|
||||||
signStub.restore();
|
});
|
||||||
pubStub.restore();
|
|
||||||
|
it('should accept the approval request', async function () {
|
||||||
|
await txController.approveTransaction(txMeta.id);
|
||||||
|
|
||||||
|
assert.equal(messengerMock.call.callCount, 1);
|
||||||
|
assert.deepEqual(messengerMock.call.getCall(0).args, [
|
||||||
|
'ApprovalController:acceptRequest',
|
||||||
|
txMeta.id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not throw if accepting approval request throws', async function () {
|
||||||
|
messengerMock.call.throws();
|
||||||
|
await txController.approveTransaction(txMeta.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1108,7 +1195,7 @@ describe('Transaction Controller', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('#cancelTransaction', function () {
|
describe('#cancelTransaction', function () {
|
||||||
it('should emit a status change to rejected', function (done) {
|
beforeEach(function () {
|
||||||
txController.txStateManager._addTransactionsToState([
|
txController.txStateManager._addTransactionsToState([
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
@ -1181,7 +1268,9 @@ describe('Transaction Controller', function () {
|
|||||||
history: [{}],
|
history: [{}],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit a status change to rejected', function (done) {
|
||||||
txController.once('tx:status-update', (txId, status) => {
|
txController.once('tx:status-update', (txId, status) => {
|
||||||
try {
|
try {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
@ -1198,6 +1287,22 @@ describe('Transaction Controller', function () {
|
|||||||
|
|
||||||
txController.cancelTransaction(0);
|
txController.cancelTransaction(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should reject the approval request', function () {
|
||||||
|
txController.cancelTransaction(0);
|
||||||
|
|
||||||
|
assert.equal(messengerMock.call.callCount, 1);
|
||||||
|
assert.deepEqual(messengerMock.call.getCall(0).args, [
|
||||||
|
'ApprovalController:rejectRequest',
|
||||||
|
'0',
|
||||||
|
new Error('Rejected'),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not throw if rejecting approval request throws', async function () {
|
||||||
|
messengerMock.call.throws();
|
||||||
|
txController.cancelTransaction(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#createSpeedUpTransaction', function () {
|
describe('#createSpeedUpTransaction', function () {
|
||||||
|
@ -1,318 +0,0 @@
|
|||||||
import EventEmitter from 'events';
|
|
||||||
import { ObservableStore } from '@metamask/obs-store';
|
|
||||||
import { ethErrors } from 'eth-rpc-errors';
|
|
||||||
import log from 'loglevel';
|
|
||||||
import { MESSAGE_TYPE } from '../../../shared/constants/app';
|
|
||||||
import { MetaMetricsEventCategory } from '../../../shared/constants/metametrics';
|
|
||||||
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
|
|
||||||
import createId from '../../../shared/modules/random-id';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents, and contains data about, an 'eth_getEncryptionPublicKey' type request. These are created when
|
|
||||||
* an eth_getEncryptionPublicKey call is requested.
|
|
||||||
*
|
|
||||||
* @typedef {object} EncryptionPublicKey
|
|
||||||
* @property {number} id An id to track and identify the message object
|
|
||||||
* @property {object} msgParams The parameters to pass to the encryptionPublicKey method once the request is
|
|
||||||
* approved.
|
|
||||||
* @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask.
|
|
||||||
* @property {string} msgParams.data A hex string conversion of the raw buffer data of the request
|
|
||||||
* @property {number} time The epoch time at which the this message was created
|
|
||||||
* @property {string} status Indicates whether the request is 'unapproved', 'approved', 'received' or 'rejected'
|
|
||||||
* @property {string} type The json-prc method for which a request has been made. A 'Message' will
|
|
||||||
* always have a 'eth_getEncryptionPublicKey' type.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default class EncryptionPublicKeyManager extends EventEmitter {
|
|
||||||
/**
|
|
||||||
* Controller in charge of managing - storing, adding, removing, updating - EncryptionPublicKey.
|
|
||||||
*
|
|
||||||
* @param {object} opts - Controller options
|
|
||||||
* @param {Function} opts.metricEvent - A function for emitting a metric event.
|
|
||||||
*/
|
|
||||||
constructor(opts) {
|
|
||||||
super();
|
|
||||||
this.memStore = new ObservableStore({
|
|
||||||
unapprovedEncryptionPublicKeyMsgs: {},
|
|
||||||
unapprovedEncryptionPublicKeyMsgCount: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.resetState = () => {
|
|
||||||
this.memStore.updateState({
|
|
||||||
unapprovedEncryptionPublicKeyMsgs: {},
|
|
||||||
unapprovedEncryptionPublicKeyMsgCount: 0,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.messages = [];
|
|
||||||
this.metricsEvent = opts.metricsEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A getter for the number of 'unapproved' EncryptionPublicKeys in this.messages
|
|
||||||
*
|
|
||||||
* @returns {number} The number of 'unapproved' EncryptionPublicKeys in this.messages
|
|
||||||
*/
|
|
||||||
get unapprovedEncryptionPublicKeyMsgCount() {
|
|
||||||
return Object.keys(this.getUnapprovedMsgs()).length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A getter for the 'unapproved' EncryptionPublicKeys in this.messages
|
|
||||||
*
|
|
||||||
* @returns {object} An index of EncryptionPublicKey ids to EncryptionPublicKeys, for all 'unapproved' EncryptionPublicKeys in
|
|
||||||
* this.messages
|
|
||||||
*/
|
|
||||||
getUnapprovedMsgs() {
|
|
||||||
return this.messages
|
|
||||||
.filter((msg) => msg.status === 'unapproved')
|
|
||||||
.reduce((result, msg) => {
|
|
||||||
result[msg.id] = msg;
|
|
||||||
return result;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new EncryptionPublicKey with an 'unapproved' status using the passed msgParams. this.addMsg is called to add
|
|
||||||
* the new EncryptionPublicKey to this.messages, and to save the unapproved EncryptionPublicKeys from that list to
|
|
||||||
* this.memStore.
|
|
||||||
*
|
|
||||||
* @param {object} address - The param for the eth_getEncryptionPublicKey call to be made after the message is approved.
|
|
||||||
* @param {object} [req] - The original request object possibly containing the origin
|
|
||||||
* @returns {Promise<Buffer>} The raw public key contents
|
|
||||||
*/
|
|
||||||
addUnapprovedMessageAsync(address, req) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!address) {
|
|
||||||
reject(new Error('MetaMask Message: address field is required.'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const msgId = this.addUnapprovedMessage(address, req);
|
|
||||||
this.once(`${msgId}:finished`, (data) => {
|
|
||||||
switch (data.status) {
|
|
||||||
case 'received':
|
|
||||||
resolve(data.rawData);
|
|
||||||
return;
|
|
||||||
case 'rejected':
|
|
||||||
reject(
|
|
||||||
ethErrors.provider.userRejectedRequest(
|
|
||||||
'MetaMask EncryptionPublicKey: User denied message EncryptionPublicKey.',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
reject(
|
|
||||||
new Error(
|
|
||||||
`MetaMask EncryptionPublicKey: Unknown problem: ${JSON.stringify(
|
|
||||||
address,
|
|
||||||
)}`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new EncryptionPublicKey with an 'unapproved' status using the passed msgParams. this.addMsg is called to add
|
|
||||||
* the new EncryptionPublicKey to this.messages, and to save the unapproved EncryptionPublicKeys from that list to
|
|
||||||
* this.memStore.
|
|
||||||
*
|
|
||||||
* @param {object} address - The param for the eth_getEncryptionPublicKey call to be made after the message is approved.
|
|
||||||
* @param {object} [req] - The original request object possibly containing the origin
|
|
||||||
* @returns {number} The id of the newly created EncryptionPublicKey.
|
|
||||||
*/
|
|
||||||
addUnapprovedMessage(address, req) {
|
|
||||||
log.debug(`EncryptionPublicKeyManager addUnapprovedMessage: address`);
|
|
||||||
// create txData obj with parameters and meta data
|
|
||||||
const time = new Date().getTime();
|
|
||||||
const msgId = createId();
|
|
||||||
const msgData = {
|
|
||||||
id: msgId,
|
|
||||||
msgParams: address,
|
|
||||||
time,
|
|
||||||
status: 'unapproved',
|
|
||||||
type: MESSAGE_TYPE.ETH_GET_ENCRYPTION_PUBLIC_KEY,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (req) {
|
|
||||||
msgData.origin = req.origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addMsg(msgData);
|
|
||||||
|
|
||||||
// signal update
|
|
||||||
this.emit('update');
|
|
||||||
return msgId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a passed EncryptionPublicKey to this.messages, and calls this._saveMsgList() to save the unapproved EncryptionPublicKeys from that
|
|
||||||
* list to this.memStore.
|
|
||||||
*
|
|
||||||
* @param {Message} msg - The EncryptionPublicKey to add to this.messages
|
|
||||||
*/
|
|
||||||
addMsg(msg) {
|
|
||||||
this.messages.push(msg);
|
|
||||||
this._saveMsgList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a specified EncryptionPublicKey.
|
|
||||||
*
|
|
||||||
* @param {number} msgId - The id of the EncryptionPublicKey to get
|
|
||||||
* @returns {EncryptionPublicKey|undefined} The EncryptionPublicKey with the id that matches the passed msgId, or undefined
|
|
||||||
* if no EncryptionPublicKey has that id.
|
|
||||||
*/
|
|
||||||
getMsg(msgId) {
|
|
||||||
return this.messages.find((msg) => msg.id === msgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Approves a EncryptionPublicKey. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise
|
|
||||||
* with any the message params modified for proper providing.
|
|
||||||
*
|
|
||||||
* @param {object} msgParams - The msgParams to be used when eth_getEncryptionPublicKey is called, plus data added by MetaMask.
|
|
||||||
* @param {object} msgParams.metamaskId - Added to msgParams for tracking and identification within MetaMask.
|
|
||||||
* @returns {Promise<object>} Promises the msgParams object with metamaskId removed.
|
|
||||||
*/
|
|
||||||
approveMessage(msgParams) {
|
|
||||||
this.setMsgStatusApproved(msgParams.metamaskId);
|
|
||||||
return this.prepMsgForEncryptionPublicKey(msgParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a EncryptionPublicKey status to 'approved' via a call to this._setMsgStatus.
|
|
||||||
*
|
|
||||||
* @param {number} msgId - The id of the EncryptionPublicKey to approve.
|
|
||||||
*/
|
|
||||||
setMsgStatusApproved(msgId) {
|
|
||||||
this._setMsgStatus(msgId, 'approved');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a EncryptionPublicKey status to 'received' via a call to this._setMsgStatus and updates that EncryptionPublicKey in
|
|
||||||
* this.messages by adding the raw data of request to the EncryptionPublicKey
|
|
||||||
*
|
|
||||||
* @param {number} msgId - The id of the EncryptionPublicKey.
|
|
||||||
* @param {buffer} rawData - The raw data of the message request
|
|
||||||
*/
|
|
||||||
setMsgStatusReceived(msgId, rawData) {
|
|
||||||
const msg = this.getMsg(msgId);
|
|
||||||
msg.rawData = rawData;
|
|
||||||
this._updateMsg(msg);
|
|
||||||
this._setMsgStatus(msgId, 'received');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams
|
|
||||||
*
|
|
||||||
* @param {object} msgParams - The msgParams to modify
|
|
||||||
* @returns {Promise<object>} Promises the msgParams with the metamaskId property removed
|
|
||||||
*/
|
|
||||||
async prepMsgForEncryptionPublicKey(msgParams) {
|
|
||||||
delete msgParams.metamaskId;
|
|
||||||
return msgParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a EncryptionPublicKey status to 'rejected' via a call to this._setMsgStatus.
|
|
||||||
*
|
|
||||||
* @param {number} msgId - The id of the EncryptionPublicKey to reject.
|
|
||||||
* @param reason
|
|
||||||
*/
|
|
||||||
rejectMsg(msgId, reason = undefined) {
|
|
||||||
if (reason) {
|
|
||||||
this.metricsEvent({
|
|
||||||
event: reason,
|
|
||||||
category: MetaMetricsEventCategory.Messages,
|
|
||||||
properties: {
|
|
||||||
action: 'Encryption public key Request',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this._setMsgStatus(msgId, 'rejected');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a TypedMessage status to 'errored' via a call to this._setMsgStatus.
|
|
||||||
*
|
|
||||||
* @param {number} msgId - The id of the TypedMessage to error
|
|
||||||
* @param error
|
|
||||||
*/
|
|
||||||
errorMessage(msgId, error) {
|
|
||||||
const msg = this.getMsg(msgId);
|
|
||||||
msg.error = error;
|
|
||||||
this._updateMsg(msg);
|
|
||||||
this._setMsgStatus(msgId, 'errored');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears all unapproved messages from memory.
|
|
||||||
*/
|
|
||||||
clearUnapproved() {
|
|
||||||
this.messages = this.messages.filter((msg) => msg.status !== 'unapproved');
|
|
||||||
this._saveMsgList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the status of a EncryptionPublicKey in this.messages via a call to this._updateMsg
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @param {number} msgId - The id of the EncryptionPublicKey to update.
|
|
||||||
* @param {string} status - The new status of the EncryptionPublicKey.
|
|
||||||
* @throws A 'EncryptionPublicKeyManager - EncryptionPublicKey not found for id: "${msgId}".' if there is no EncryptionPublicKey
|
|
||||||
* in this.messages with an id equal to the passed msgId
|
|
||||||
* @fires An event with a name equal to `${msgId}:${status}`. The EncryptionPublicKey is also fired.
|
|
||||||
* @fires If status is 'rejected' or 'received', an event with a name equal to `${msgId}:finished` is fired along
|
|
||||||
* with the EncryptionPublicKey
|
|
||||||
*/
|
|
||||||
_setMsgStatus(msgId, status) {
|
|
||||||
const msg = this.getMsg(msgId);
|
|
||||||
if (!msg) {
|
|
||||||
throw new Error(
|
|
||||||
`EncryptionPublicKeyManager - Message not found for id: "${msgId}".`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
msg.status = status;
|
|
||||||
this._updateMsg(msg);
|
|
||||||
this.emit(`${msgId}:${status}`, msg);
|
|
||||||
if (status === 'rejected' || status === 'received') {
|
|
||||||
this.emit(`${msgId}:finished`, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a EncryptionPublicKey in this.messages to the passed EncryptionPublicKey if the ids are equal. Then saves the
|
|
||||||
* unapprovedEncryptionPublicKeyMsgs index to storage via this._saveMsgList
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @param {EncryptionPublicKey} msg - A EncryptionPublicKey that will replace an existing EncryptionPublicKey (with the same
|
|
||||||
* id) in this.messages
|
|
||||||
*/
|
|
||||||
_updateMsg(msg) {
|
|
||||||
const index = this.messages.findIndex((message) => message.id === msg.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.messages[index] = msg;
|
|
||||||
}
|
|
||||||
this._saveMsgList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the unapproved EncryptionPublicKeys, and their count, to this.memStore
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @fires 'updateBadge'
|
|
||||||
*/
|
|
||||||
_saveMsgList() {
|
|
||||||
const unapprovedEncryptionPublicKeyMsgs = this.getUnapprovedMsgs();
|
|
||||||
const unapprovedEncryptionPublicKeyMsgCount = Object.keys(
|
|
||||||
unapprovedEncryptionPublicKeyMsgs,
|
|
||||||
).length;
|
|
||||||
this.memStore.updateState({
|
|
||||||
unapprovedEncryptionPublicKeyMsgs,
|
|
||||||
unapprovedEncryptionPublicKeyMsgCount,
|
|
||||||
});
|
|
||||||
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
|
|
||||||
}
|
|
||||||
}
|
|
@ -97,8 +97,8 @@ import {
|
|||||||
import { MILLISECOND, SECOND } from '../../shared/constants/time';
|
import { MILLISECOND, SECOND } from '../../shared/constants/time';
|
||||||
import {
|
import {
|
||||||
ORIGIN_METAMASK,
|
ORIGIN_METAMASK,
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
|
||||||
MESSAGE_TYPE,
|
MESSAGE_TYPE,
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||||
SNAP_DIALOG_TYPES,
|
SNAP_DIALOG_TYPES,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
POLLING_TOKEN_ENVIRONMENT_TYPES,
|
POLLING_TOKEN_ENVIRONMENT_TYPES,
|
||||||
@ -143,8 +143,9 @@ import createTabIdMiddleware from './lib/createTabIdMiddleware';
|
|||||||
import createOnboardingMiddleware from './lib/createOnboardingMiddleware';
|
import createOnboardingMiddleware from './lib/createOnboardingMiddleware';
|
||||||
import { setupMultiplex } from './lib/stream-utils';
|
import { setupMultiplex } from './lib/stream-utils';
|
||||||
import EnsController from './controllers/ens';
|
import EnsController from './controllers/ens';
|
||||||
import NetworkController, {
|
import {
|
||||||
NetworkControllerEventTypes,
|
NetworkController,
|
||||||
|
NetworkControllerEventType,
|
||||||
} from './controllers/network';
|
} from './controllers/network';
|
||||||
import PreferencesController from './controllers/preferences';
|
import PreferencesController from './controllers/preferences';
|
||||||
import AppStateController from './controllers/app-state';
|
import AppStateController from './controllers/app-state';
|
||||||
@ -154,7 +155,6 @@ import OnboardingController from './controllers/onboarding';
|
|||||||
import BackupController from './controllers/backup';
|
import BackupController from './controllers/backup';
|
||||||
import IncomingTransactionsController from './controllers/incoming-transactions';
|
import IncomingTransactionsController from './controllers/incoming-transactions';
|
||||||
import DecryptMessageManager from './lib/decrypt-message-manager';
|
import DecryptMessageManager from './lib/decrypt-message-manager';
|
||||||
import EncryptionPublicKeyManager from './lib/encryption-public-key-manager';
|
|
||||||
import TransactionController from './controllers/transactions';
|
import TransactionController from './controllers/transactions';
|
||||||
import DetectTokensController from './controllers/detect-tokens';
|
import DetectTokensController from './controllers/detect-tokens';
|
||||||
import SwapsController from './controllers/swaps';
|
import SwapsController from './controllers/swaps';
|
||||||
@ -166,6 +166,7 @@ import createMetaRPCHandler from './lib/createMetaRPCHandler';
|
|||||||
import { previousValueComparator } from './lib/util';
|
import { previousValueComparator } from './lib/util';
|
||||||
import createMetamaskMiddleware from './lib/createMetamaskMiddleware';
|
import createMetamaskMiddleware from './lib/createMetamaskMiddleware';
|
||||||
import SignController from './controllers/sign';
|
import SignController from './controllers/sign';
|
||||||
|
import EncryptionPublicKeyController from './controllers/encryption-public-key';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CaveatMutatorFactories,
|
CaveatMutatorFactories,
|
||||||
@ -259,11 +260,16 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
name: 'ApprovalController',
|
name: 'ApprovalController',
|
||||||
}),
|
}),
|
||||||
showApprovalRequest: opts.showUserConfirmation,
|
showApprovalRequest: opts.showUserConfirmation,
|
||||||
|
typesExcludedFromRateLimiting: [
|
||||||
|
MESSAGE_TYPE.ETH_SIGN,
|
||||||
|
MESSAGE_TYPE.PERSONAL_SIGN,
|
||||||
|
MESSAGE_TYPE.ETH_SIGN_TYPED_DATA,
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const networkControllerMessenger = this.controllerMessenger.getRestricted({
|
const networkControllerMessenger = this.controllerMessenger.getRestricted({
|
||||||
name: 'NetworkController',
|
name: 'NetworkController',
|
||||||
allowedEvents: Object.values(NetworkControllerEventTypes),
|
allowedEvents: Object.values(NetworkControllerEventType),
|
||||||
});
|
});
|
||||||
this.networkController = new NetworkController({
|
this.networkController = new NetworkController({
|
||||||
messenger: networkControllerMessenger,
|
messenger: networkControllerMessenger,
|
||||||
@ -310,11 +316,11 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
initLangCode: opts.initLangCode,
|
initLangCode: opts.initLangCode,
|
||||||
onInfuraIsBlocked: networkControllerMessenger.subscribe.bind(
|
onInfuraIsBlocked: networkControllerMessenger.subscribe.bind(
|
||||||
networkControllerMessenger,
|
networkControllerMessenger,
|
||||||
NetworkControllerEventTypes.InfuraIsBlocked,
|
NetworkControllerEventType.InfuraIsBlocked,
|
||||||
),
|
),
|
||||||
onInfuraIsUnblocked: networkControllerMessenger.subscribe.bind(
|
onInfuraIsUnblocked: networkControllerMessenger.subscribe.bind(
|
||||||
networkControllerMessenger,
|
networkControllerMessenger,
|
||||||
NetworkControllerEventTypes.InfuraIsUnblocked,
|
NetworkControllerEventType.InfuraIsUnblocked,
|
||||||
),
|
),
|
||||||
tokenListController: this.tokenListController,
|
tokenListController: this.tokenListController,
|
||||||
provider: this.provider,
|
provider: this.provider,
|
||||||
@ -452,7 +458,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
preferencesStore: this.preferencesController.store,
|
preferencesStore: this.preferencesController.store,
|
||||||
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
|
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
|
||||||
networkControllerMessenger,
|
networkControllerMessenger,
|
||||||
NetworkControllerEventTypes.NetworkDidChange,
|
NetworkControllerEventType.NetworkDidChange,
|
||||||
),
|
),
|
||||||
getNetworkIdentifier: () => {
|
getNetworkIdentifier: () => {
|
||||||
const { type, rpcUrl } =
|
const { type, rpcUrl } =
|
||||||
@ -491,7 +497,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
// onNetworkDidChange
|
// onNetworkDidChange
|
||||||
onNetworkStateChange: networkControllerMessenger.subscribe.bind(
|
onNetworkStateChange: networkControllerMessenger.subscribe.bind(
|
||||||
networkControllerMessenger,
|
networkControllerMessenger,
|
||||||
NetworkControllerEventTypes.NetworkDidChange,
|
NetworkControllerEventType.NetworkDidChange,
|
||||||
),
|
),
|
||||||
getCurrentNetworkEIP1559Compatibility:
|
getCurrentNetworkEIP1559Compatibility:
|
||||||
this.networkController.getEIP1559Compatibility.bind(
|
this.networkController.getEIP1559Compatibility.bind(
|
||||||
@ -519,9 +525,15 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
isUnlocked: this.isUnlocked.bind(this),
|
isUnlocked: this.isUnlocked.bind(this),
|
||||||
initState: initState.AppStateController,
|
initState: initState.AppStateController,
|
||||||
onInactiveTimeout: () => this.setLocked(),
|
onInactiveTimeout: () => this.setLocked(),
|
||||||
showUnlockRequest: opts.showUserConfirmation,
|
|
||||||
preferencesStore: this.preferencesController.store,
|
preferencesStore: this.preferencesController.store,
|
||||||
qrHardwareStore: this.qrHardwareKeyring.getMemStore(),
|
qrHardwareStore: this.qrHardwareKeyring.getMemStore(),
|
||||||
|
messenger: this.controllerMessenger.getRestricted({
|
||||||
|
name: 'AppStateController',
|
||||||
|
allowedActions: [
|
||||||
|
`${this.approvalController.name}:addRequest`,
|
||||||
|
`${this.approvalController.name}:acceptRequest`,
|
||||||
|
],
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const currencyRateMessenger = this.controllerMessenger.getRestricted({
|
const currencyRateMessenger = this.controllerMessenger.getRestricted({
|
||||||
@ -609,7 +621,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
this.networkController.store.getState().provider.chainId,
|
this.networkController.store.getState().provider.chainId,
|
||||||
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
|
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
|
||||||
networkControllerMessenger,
|
networkControllerMessenger,
|
||||||
NetworkControllerEventTypes.NetworkDidChange,
|
NetworkControllerEventType.NetworkDidChange,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -621,7 +633,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
blockTracker: this.blockTracker,
|
blockTracker: this.blockTracker,
|
||||||
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
|
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
|
||||||
networkControllerMessenger,
|
networkControllerMessenger,
|
||||||
NetworkControllerEventTypes.NetworkDidChange,
|
NetworkControllerEventType.NetworkDidChange,
|
||||||
),
|
),
|
||||||
getCurrentChainId: () =>
|
getCurrentChainId: () =>
|
||||||
this.networkController.store.getState().provider.chainId,
|
this.networkController.store.getState().provider.chainId,
|
||||||
@ -1007,8 +1019,15 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
getDeviceModel: this.getDeviceModel.bind(this),
|
getDeviceModel: this.getDeviceModel.bind(this),
|
||||||
getTokenStandardAndDetails: this.getTokenStandardAndDetails.bind(this),
|
getTokenStandardAndDetails: this.getTokenStandardAndDetails.bind(this),
|
||||||
securityProviderRequest: this.securityProviderRequest.bind(this),
|
securityProviderRequest: this.securityProviderRequest.bind(this),
|
||||||
|
messenger: this.controllerMessenger.getRestricted({
|
||||||
|
name: 'TransactionController',
|
||||||
|
allowedActions: [
|
||||||
|
`${this.approvalController.name}:addRequest`,
|
||||||
|
`${this.approvalController.name}:acceptRequest`,
|
||||||
|
`${this.approvalController.name}:rejectRequest`,
|
||||||
|
],
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
this.txController.on('newUnapprovedTx', () => opts.showUserConfirmation());
|
|
||||||
|
|
||||||
this.txController.on(`tx:status-update`, async (txId, status) => {
|
this.txController.on(`tx:status-update`, async (txId, status) => {
|
||||||
if (
|
if (
|
||||||
@ -1099,7 +1118,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
networkControllerMessenger.subscribe(
|
networkControllerMessenger.subscribe(
|
||||||
NetworkControllerEventTypes.NetworkDidChange,
|
NetworkControllerEventType.NetworkDidChange,
|
||||||
async () => {
|
async () => {
|
||||||
const { ticker } = this.networkController.store.getState().provider;
|
const { ticker } = this.networkController.store.getState().provider;
|
||||||
try {
|
try {
|
||||||
@ -1117,7 +1136,18 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
this.metaMetricsController,
|
this.metaMetricsController,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
this.encryptionPublicKeyManager = new EncryptionPublicKeyManager({
|
|
||||||
|
this.encryptionPublicKeyController = new EncryptionPublicKeyController({
|
||||||
|
messenger: this.controllerMessenger.getRestricted({
|
||||||
|
name: 'EncryptionPublicKeyController',
|
||||||
|
allowedActions: [
|
||||||
|
`${this.approvalController.name}:addRequest`,
|
||||||
|
`${this.approvalController.name}:acceptRequest`,
|
||||||
|
`${this.approvalController.name}:rejectRequest`,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
keyringController: this.keyringController,
|
||||||
|
getState: this.getState.bind(this),
|
||||||
metricsEvent: this.metaMetricsController.trackEvent.bind(
|
metricsEvent: this.metaMetricsController.trackEvent.bind(
|
||||||
this.metaMetricsController,
|
this.metaMetricsController,
|
||||||
),
|
),
|
||||||
@ -1145,7 +1175,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
networkController: this.networkController,
|
networkController: this.networkController,
|
||||||
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
|
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
|
||||||
networkControllerMessenger,
|
networkControllerMessenger,
|
||||||
NetworkControllerEventTypes.NetworkDidChange,
|
NetworkControllerEventType.NetworkDidChange,
|
||||||
),
|
),
|
||||||
provider: this.provider,
|
provider: this.provider,
|
||||||
getProviderConfig: () => this.networkController.store.getState().provider,
|
getProviderConfig: () => this.networkController.store.getState().provider,
|
||||||
@ -1188,7 +1218,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
|
|
||||||
// ensure accountTracker updates balances after network change
|
// ensure accountTracker updates balances after network change
|
||||||
networkControllerMessenger.subscribe(
|
networkControllerMessenger.subscribe(
|
||||||
NetworkControllerEventTypes.NetworkDidChange,
|
NetworkControllerEventType.NetworkDidChange,
|
||||||
() => {
|
() => {
|
||||||
this.accountTracker._updateAccounts();
|
this.accountTracker._updateAccounts();
|
||||||
},
|
},
|
||||||
@ -1196,10 +1226,10 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
|
|
||||||
// clear unapproved transactions and messages when the network will change
|
// clear unapproved transactions and messages when the network will change
|
||||||
networkControllerMessenger.subscribe(
|
networkControllerMessenger.subscribe(
|
||||||
NetworkControllerEventTypes.NetworkWillChange,
|
NetworkControllerEventType.NetworkWillChange,
|
||||||
() => {
|
() => {
|
||||||
this.txController.txStateManager.clearUnapprovedTxs();
|
this.txController.txStateManager.clearUnapprovedTxs();
|
||||||
this.encryptionPublicKeyManager.clearUnapproved();
|
this.encryptionPublicKeyController.clearUnapproved();
|
||||||
this.decryptMessageManager.clearUnapproved();
|
this.decryptMessageManager.clearUnapproved();
|
||||||
this.signController.clearUnapproved();
|
this.signController.clearUnapproved();
|
||||||
},
|
},
|
||||||
@ -1266,7 +1296,10 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
this.signController,
|
this.signController,
|
||||||
),
|
),
|
||||||
processDecryptMessage: this.newRequestDecryptMessage.bind(this),
|
processDecryptMessage: this.newRequestDecryptMessage.bind(this),
|
||||||
processEncryptionPublicKey: this.newRequestEncryptionPublicKey.bind(this),
|
processEncryptionPublicKey:
|
||||||
|
this.encryptionPublicKeyController.newRequestEncryptionPublicKey.bind(
|
||||||
|
this.encryptionPublicKeyController,
|
||||||
|
),
|
||||||
getPendingNonce: this.getPendingNonce.bind(this),
|
getPendingNonce: this.getPendingNonce.bind(this),
|
||||||
getPendingTransactionByHash: (hash) =>
|
getPendingTransactionByHash: (hash) =>
|
||||||
this.txController.getTransactions({
|
this.txController.getTransactions({
|
||||||
@ -1289,7 +1322,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
TxController: this.txController.memStore,
|
TxController: this.txController.memStore,
|
||||||
TokenRatesController: this.tokenRatesController,
|
TokenRatesController: this.tokenRatesController,
|
||||||
DecryptMessageManager: this.decryptMessageManager.memStore,
|
DecryptMessageManager: this.decryptMessageManager.memStore,
|
||||||
EncryptionPublicKeyManager: this.encryptionPublicKeyManager.memStore,
|
EncryptionPublicKeyController: this.encryptionPublicKeyController,
|
||||||
SignController: this.signController,
|
SignController: this.signController,
|
||||||
SwapsController: this.swapsController.store,
|
SwapsController: this.swapsController.store,
|
||||||
EnsController: this.ensController.store,
|
EnsController: this.ensController.store,
|
||||||
@ -1371,7 +1404,9 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
this.accountTracker.resetState,
|
this.accountTracker.resetState,
|
||||||
this.txController.resetState,
|
this.txController.resetState,
|
||||||
this.decryptMessageManager.resetState,
|
this.decryptMessageManager.resetState,
|
||||||
this.encryptionPublicKeyManager.resetState,
|
this.encryptionPublicKeyController.resetState.bind(
|
||||||
|
this.encryptionPublicKeyController,
|
||||||
|
),
|
||||||
this.signController.resetState.bind(this.signController),
|
this.signController.resetState.bind(this.signController),
|
||||||
this.swapsController.resetState,
|
this.swapsController.resetState,
|
||||||
this.ensController.resetState,
|
this.ensController.resetState,
|
||||||
@ -1994,6 +2029,16 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
appStateController.setConnectedStatusPopoverHasBeenShown.bind(
|
appStateController.setConnectedStatusPopoverHasBeenShown.bind(
|
||||||
appStateController,
|
appStateController,
|
||||||
),
|
),
|
||||||
|
setRecoveryPhraseReminderHasBeenShown:
|
||||||
|
appStateController.setRecoveryPhraseReminderHasBeenShown.bind(
|
||||||
|
appStateController,
|
||||||
|
),
|
||||||
|
setRecoveryPhraseReminderLastShown:
|
||||||
|
appStateController.setRecoveryPhraseReminderLastShown.bind(
|
||||||
|
appStateController,
|
||||||
|
),
|
||||||
|
setTermsOfUseLastAgreed:
|
||||||
|
appStateController.setTermsOfUseLastAgreed.bind(appStateController),
|
||||||
setOutdatedBrowserWarningLastShown:
|
setOutdatedBrowserWarningLastShown:
|
||||||
appStateController.setOutdatedBrowserWarningLastShown.bind(
|
appStateController.setOutdatedBrowserWarningLastShown.bind(
|
||||||
appStateController,
|
appStateController,
|
||||||
@ -2074,9 +2119,15 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
decryptMessageInline: this.decryptMessageInline.bind(this),
|
decryptMessageInline: this.decryptMessageInline.bind(this),
|
||||||
cancelDecryptMessage: this.cancelDecryptMessage.bind(this),
|
cancelDecryptMessage: this.cancelDecryptMessage.bind(this),
|
||||||
|
|
||||||
// EncryptionPublicKeyManager
|
// EncryptionPublicKeyController
|
||||||
encryptionPublicKey: this.encryptionPublicKey.bind(this),
|
encryptionPublicKey:
|
||||||
cancelEncryptionPublicKey: this.cancelEncryptionPublicKey.bind(this),
|
this.encryptionPublicKeyController.encryptionPublicKey.bind(
|
||||||
|
this.encryptionPublicKeyController,
|
||||||
|
),
|
||||||
|
cancelEncryptionPublicKey:
|
||||||
|
this.encryptionPublicKeyController.cancelEncryptionPublicKey.bind(
|
||||||
|
this.encryptionPublicKeyController,
|
||||||
|
),
|
||||||
|
|
||||||
// onboarding controller
|
// onboarding controller
|
||||||
setSeedPhraseBackedUp:
|
setSeedPhraseBackedUp:
|
||||||
@ -3301,109 +3352,6 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
return this.getState();
|
return this.getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// eth_getEncryptionPublicKey methods
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a dapp uses the eth_getEncryptionPublicKey method.
|
|
||||||
*
|
|
||||||
* @param {object} msgParams - The params of the message to sign & return to the Dapp.
|
|
||||||
* @param {object} req - (optional) the original request, containing the origin
|
|
||||||
* Passed back to the requesting Dapp.
|
|
||||||
*/
|
|
||||||
async newRequestEncryptionPublicKey(msgParams, req) {
|
|
||||||
const address = msgParams;
|
|
||||||
const keyring = await this.keyringController.getKeyringForAccount(address);
|
|
||||||
|
|
||||||
switch (keyring.type) {
|
|
||||||
case KeyringType.ledger: {
|
|
||||||
return new Promise((_, reject) => {
|
|
||||||
reject(
|
|
||||||
new Error('Ledger does not support eth_getEncryptionPublicKey.'),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
case KeyringType.trezor: {
|
|
||||||
return new Promise((_, reject) => {
|
|
||||||
reject(
|
|
||||||
new Error('Trezor does not support eth_getEncryptionPublicKey.'),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
case KeyringType.lattice: {
|
|
||||||
return new Promise((_, reject) => {
|
|
||||||
reject(
|
|
||||||
new Error('Lattice does not support eth_getEncryptionPublicKey.'),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
case KeyringType.qr: {
|
|
||||||
return Promise.reject(
|
|
||||||
new Error('QR hardware does not support eth_getEncryptionPublicKey.'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
const promise =
|
|
||||||
this.encryptionPublicKeyManager.addUnapprovedMessageAsync(
|
|
||||||
msgParams,
|
|
||||||
req,
|
|
||||||
);
|
|
||||||
this.sendUpdate();
|
|
||||||
this.opts.showUserConfirmation();
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signifies a user's approval to receiving encryption public key in queue.
|
|
||||||
* Triggers receiving, and the callback function from newUnsignedEncryptionPublicKey.
|
|
||||||
*
|
|
||||||
* @param {object} msgParams - The params of the message to receive & return to the Dapp.
|
|
||||||
* @returns {Promise<object>} A full state update.
|
|
||||||
*/
|
|
||||||
async encryptionPublicKey(msgParams) {
|
|
||||||
log.info('MetaMaskController - encryptionPublicKey');
|
|
||||||
const msgId = msgParams.metamaskId;
|
|
||||||
// sets the status op the message to 'approved'
|
|
||||||
// and removes the metamaskId for decryption
|
|
||||||
try {
|
|
||||||
const params = await this.encryptionPublicKeyManager.approveMessage(
|
|
||||||
msgParams,
|
|
||||||
);
|
|
||||||
|
|
||||||
// EncryptionPublicKey message
|
|
||||||
const publicKey = await this.keyringController.getEncryptionPublicKey(
|
|
||||||
params.data,
|
|
||||||
);
|
|
||||||
|
|
||||||
// tells the listener that the message has been processed
|
|
||||||
// and can be returned to the dapp
|
|
||||||
this.encryptionPublicKeyManager.setMsgStatusReceived(msgId, publicKey);
|
|
||||||
} catch (error) {
|
|
||||||
log.info(
|
|
||||||
'MetaMaskController - eth_getEncryptionPublicKey failed.',
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
this.encryptionPublicKeyManager.errorMessage(msgId, error);
|
|
||||||
}
|
|
||||||
return this.getState();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to cancel a eth_getEncryptionPublicKey type message.
|
|
||||||
*
|
|
||||||
* @param {string} msgId - The ID of the message to cancel.
|
|
||||||
*/
|
|
||||||
cancelEncryptionPublicKey(msgId) {
|
|
||||||
const messageManager = this.encryptionPublicKeyManager;
|
|
||||||
messageManager.rejectMsg(msgId);
|
|
||||||
return this.getState();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {boolean} true if the keyring type supports EIP-1559
|
* @returns {boolean} true if the keyring type supports EIP-1559
|
||||||
*/
|
*/
|
||||||
|
254
app/scripts/migrations/084.test.js
Normal file
254
app/scripts/migrations/084.test.js
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
import { v4 } from 'uuid';
|
||||||
|
import { migrate, version } from './084';
|
||||||
|
|
||||||
|
jest.mock('uuid', () => {
|
||||||
|
const actual = jest.requireActual('uuid');
|
||||||
|
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
v4: jest.fn(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('migration #84', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
v4.mockImplementationOnce(() => 'network-configuration-id-1')
|
||||||
|
.mockImplementationOnce(() => 'network-configuration-id-2')
|
||||||
|
.mockImplementationOnce(() => 'network-configuration-id-3')
|
||||||
|
.mockImplementationOnce(() => 'network-configuration-id-4');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
it('should update the version metadata', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 83,
|
||||||
|
},
|
||||||
|
data: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const newStorage = await migrate(oldStorage);
|
||||||
|
expect(newStorage.meta).toStrictEqual({
|
||||||
|
version,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the key of the networkConfigurations object to set the id of each network configuration', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
NetworkController: {
|
||||||
|
networkConfigurations: {
|
||||||
|
'network-configuration-id-1': {
|
||||||
|
chainId: '0x539',
|
||||||
|
nickname: 'Localhost 8545',
|
||||||
|
rpcPrefs: {},
|
||||||
|
rpcUrl: 'http://localhost:8545',
|
||||||
|
ticker: 'ETH',
|
||||||
|
},
|
||||||
|
'network-configuration-id-2': {
|
||||||
|
chainId: '0xa4b1',
|
||||||
|
nickname: 'Arbitrum One',
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://explorer.arbitrum.io',
|
||||||
|
},
|
||||||
|
rpcUrl:
|
||||||
|
'https://arbitrum-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
|
||||||
|
ticker: 'ETH',
|
||||||
|
},
|
||||||
|
'network-configuration-id-3': {
|
||||||
|
chainId: '0x4e454152',
|
||||||
|
nickname: 'Aurora Mainnet',
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://aurorascan.dev/',
|
||||||
|
},
|
||||||
|
rpcUrl:
|
||||||
|
'https://aurora-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
|
||||||
|
ticker: 'Aurora ETH',
|
||||||
|
},
|
||||||
|
'network-configuration-id-4': {
|
||||||
|
chainId: '0x38',
|
||||||
|
nickname:
|
||||||
|
'BNB Smart Chain (previously Binance Smart Chain Mainnet)',
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://bscscan.com/',
|
||||||
|
},
|
||||||
|
rpcUrl: 'https://bsc-dataseed.binance.org/',
|
||||||
|
ticker: 'BNB',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const newStorage = await migrate(oldStorage);
|
||||||
|
|
||||||
|
const expectedNewStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
NetworkController: {
|
||||||
|
networkConfigurations: {
|
||||||
|
'network-configuration-id-1': {
|
||||||
|
chainId: '0x539',
|
||||||
|
nickname: 'Localhost 8545',
|
||||||
|
rpcPrefs: {},
|
||||||
|
rpcUrl: 'http://localhost:8545',
|
||||||
|
ticker: 'ETH',
|
||||||
|
id: 'network-configuration-id-1',
|
||||||
|
},
|
||||||
|
'network-configuration-id-2': {
|
||||||
|
chainId: '0xa4b1',
|
||||||
|
nickname: 'Arbitrum One',
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://explorer.arbitrum.io',
|
||||||
|
},
|
||||||
|
rpcUrl:
|
||||||
|
'https://arbitrum-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
|
||||||
|
ticker: 'ETH',
|
||||||
|
id: 'network-configuration-id-2',
|
||||||
|
},
|
||||||
|
'network-configuration-id-3': {
|
||||||
|
chainId: '0x4e454152',
|
||||||
|
nickname: 'Aurora Mainnet',
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://aurorascan.dev/',
|
||||||
|
},
|
||||||
|
rpcUrl:
|
||||||
|
'https://aurora-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
|
||||||
|
ticker: 'Aurora ETH',
|
||||||
|
id: 'network-configuration-id-3',
|
||||||
|
},
|
||||||
|
'network-configuration-id-4': {
|
||||||
|
chainId: '0x38',
|
||||||
|
nickname:
|
||||||
|
'BNB Smart Chain (previously Binance Smart Chain Mainnet)',
|
||||||
|
rpcPrefs: {
|
||||||
|
blockExplorerUrl: 'https://bscscan.com/',
|
||||||
|
},
|
||||||
|
rpcUrl: 'https://bsc-dataseed.binance.org/',
|
||||||
|
ticker: 'BNB',
|
||||||
|
id: 'network-configuration-id-4',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(newStorage).toStrictEqual(expectedNewStorage);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify state if state.NetworkController is undefined', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
testProperty: 'testValue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const newStorage = await migrate(oldStorage);
|
||||||
|
|
||||||
|
const expectedNewStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
testProperty: 'testValue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(newStorage).toStrictEqual(expectedNewStorage);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify state if state.NetworkController is not an object', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
NetworkController: false,
|
||||||
|
testProperty: 'testValue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const newStorage = await migrate(oldStorage);
|
||||||
|
|
||||||
|
const expectedNewStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
NetworkController: false,
|
||||||
|
testProperty: 'testValue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(newStorage).toStrictEqual(expectedNewStorage);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify state if state.NetworkController.networkConfigurations is undefined', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
NetworkController: {
|
||||||
|
testNetworkControllerProperty: 'testNetworkControllerValue',
|
||||||
|
networkConfigurations: undefined,
|
||||||
|
},
|
||||||
|
testProperty: 'testValue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const newStorage = await migrate(oldStorage);
|
||||||
|
|
||||||
|
const expectedNewStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
NetworkController: {
|
||||||
|
testNetworkControllerProperty: 'testNetworkControllerValue',
|
||||||
|
networkConfigurations: undefined,
|
||||||
|
},
|
||||||
|
testProperty: 'testValue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(newStorage).toStrictEqual(expectedNewStorage);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify state if state.NetworkController.networkConfigurations is an empty object', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
NetworkController: {
|
||||||
|
testNetworkControllerProperty: 'testNetworkControllerValue',
|
||||||
|
networkConfigurations: {},
|
||||||
|
},
|
||||||
|
testProperty: 'testValue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const newStorage = await migrate(oldStorage);
|
||||||
|
|
||||||
|
const expectedNewStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
NetworkController: {
|
||||||
|
testNetworkControllerProperty: 'testNetworkControllerValue',
|
||||||
|
networkConfigurations: {},
|
||||||
|
},
|
||||||
|
testProperty: 'testValue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(newStorage).toStrictEqual(expectedNewStorage);
|
||||||
|
});
|
||||||
|
});
|
58
app/scripts/migrations/084.ts
Normal file
58
app/scripts/migrations/084.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { isObject } from '@metamask/utils';
|
||||||
|
|
||||||
|
export const version = 84;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that each networkConfigurations object in state.NetworkController.networkConfigurations has an
|
||||||
|
* `id` property which matches the key pointing that object
|
||||||
|
*
|
||||||
|
* @param originalVersionedData - Versioned MetaMask extension state, exactly what we persist to dist.
|
||||||
|
* @param originalVersionedData.meta - State metadata.
|
||||||
|
* @param originalVersionedData.meta.version - The current state version.
|
||||||
|
* @param originalVersionedData.data - The persisted MetaMask state, keyed by controller.
|
||||||
|
* @returns Updated versioned MetaMask extension state.
|
||||||
|
*/
|
||||||
|
export async function migrate(originalVersionedData: {
|
||||||
|
meta: { version: number };
|
||||||
|
data: Record<string, unknown>;
|
||||||
|
}) {
|
||||||
|
const versionedData = cloneDeep(originalVersionedData);
|
||||||
|
versionedData.meta.version = version;
|
||||||
|
versionedData.data = transformState(versionedData.data);
|
||||||
|
return versionedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformState(state: Record<string, unknown>) {
|
||||||
|
if (!isObject(state.NetworkController)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
const { NetworkController } = state;
|
||||||
|
|
||||||
|
if (!isObject(NetworkController.networkConfigurations)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { networkConfigurations } = NetworkController;
|
||||||
|
|
||||||
|
const newNetworkConfigurations: Record<string, Record<string, unknown>> = {};
|
||||||
|
|
||||||
|
for (const networkConfigurationId of Object.keys(networkConfigurations)) {
|
||||||
|
const networkConfiguration = networkConfigurations[networkConfigurationId];
|
||||||
|
if (!isObject(networkConfiguration)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
newNetworkConfigurations[networkConfigurationId] = {
|
||||||
|
...networkConfiguration,
|
||||||
|
id: networkConfigurationId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
NetworkController: {
|
||||||
|
...NetworkController,
|
||||||
|
networkConfigurations: newNetworkConfigurations,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
91
app/scripts/migrations/085.test.js
Normal file
91
app/scripts/migrations/085.test.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { migrate, version } from './085';
|
||||||
|
|
||||||
|
jest.mock('uuid', () => {
|
||||||
|
const actual = jest.requireActual('uuid');
|
||||||
|
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
v4: jest.fn(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('migration #85', () => {
|
||||||
|
it('should update the version metadata', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 84,
|
||||||
|
},
|
||||||
|
data: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const newStorage = await migrate(oldStorage);
|
||||||
|
expect(newStorage.meta).toStrictEqual({
|
||||||
|
version,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return state unaltered if there is no network controller state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 84,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
const newStorage = await migrate(oldStorage);
|
||||||
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return state unaltered if there is no network controller previous provider state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
provider: {
|
||||||
|
some: 'provider',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 84,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
const newStorage = await migrate(oldStorage);
|
||||||
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove the previous provider state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
previousProviderStore: {
|
||||||
|
example: 'config',
|
||||||
|
},
|
||||||
|
provider: {
|
||||||
|
some: 'provider',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 84,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
const newStorage = await migrate(oldStorage);
|
||||||
|
expect(newStorage.data).toStrictEqual({
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
provider: {
|
||||||
|
some: 'provider',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
33
app/scripts/migrations/085.ts
Normal file
33
app/scripts/migrations/085.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { isObject } from '@metamask/utils';
|
||||||
|
|
||||||
|
export const version = 85;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the now-obsolete network controller `previousProviderStore` state.
|
||||||
|
*
|
||||||
|
* @param originalVersionedData - Versioned MetaMask extension state, exactly what we persist to dist.
|
||||||
|
* @param originalVersionedData.meta - State metadata.
|
||||||
|
* @param originalVersionedData.meta.version - The current state version.
|
||||||
|
* @param originalVersionedData.data - The persisted MetaMask state, keyed by controller.
|
||||||
|
* @returns Updated versioned MetaMask extension state.
|
||||||
|
*/
|
||||||
|
export async function migrate(originalVersionedData: {
|
||||||
|
meta: { version: number };
|
||||||
|
data: Record<string, unknown>;
|
||||||
|
}) {
|
||||||
|
const versionedData = cloneDeep(originalVersionedData);
|
||||||
|
versionedData.meta.version = version;
|
||||||
|
versionedData.data = transformState(versionedData.data);
|
||||||
|
return versionedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformState(state: Record<string, unknown>) {
|
||||||
|
if (!isObject(state.NetworkController)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete state.NetworkController.previousProviderStore;
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
@ -87,6 +87,8 @@ import m080 from './080';
|
|||||||
import * as m081 from './081';
|
import * as m081 from './081';
|
||||||
import * as m082 from './082';
|
import * as m082 from './082';
|
||||||
import * as m083 from './083';
|
import * as m083 from './083';
|
||||||
|
import * as m084 from './084';
|
||||||
|
import * as m085 from './085';
|
||||||
|
|
||||||
const migrations = [
|
const migrations = [
|
||||||
m002,
|
m002,
|
||||||
@ -171,6 +173,8 @@ const migrations = [
|
|||||||
m081,
|
m081,
|
||||||
m082,
|
m082,
|
||||||
m083,
|
m083,
|
||||||
|
m084,
|
||||||
|
m085,
|
||||||
];
|
];
|
||||||
|
|
||||||
export default migrations;
|
export default migrations;
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
// subset of files to check against these targets.
|
// subset of files to check against these targets.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
global: {
|
global: {
|
||||||
lines: 66,
|
lines: 67.8,
|
||||||
branches: 54.4,
|
branches: 55.84,
|
||||||
statements: 65,
|
statements: 67.13,
|
||||||
functions: 58.5,
|
functions: 59.66,
|
||||||
},
|
},
|
||||||
transforms: {
|
transforms: {
|
||||||
branches: 100,
|
branches: 100,
|
||||||
|
@ -2,6 +2,7 @@ module.exports = {
|
|||||||
collectCoverageFrom: [
|
collectCoverageFrom: [
|
||||||
'<rootDir>/app/scripts/constants/error-utils.js',
|
'<rootDir>/app/scripts/constants/error-utils.js',
|
||||||
'<rootDir>/app/scripts/controllers/network/**/*.js',
|
'<rootDir>/app/scripts/controllers/network/**/*.js',
|
||||||
|
'<rootDir>/app/scripts/controllers/network/**/*.ts',
|
||||||
'<rootDir>/app/scripts/controllers/permissions/**/*.js',
|
'<rootDir>/app/scripts/controllers/permissions/**/*.js',
|
||||||
'<rootDir>/app/scripts/controllers/sign.ts',
|
'<rootDir>/app/scripts/controllers/sign.ts',
|
||||||
'<rootDir>/app/scripts/flask/**/*.js',
|
'<rootDir>/app/scripts/flask/**/*.js',
|
||||||
@ -39,6 +40,7 @@ module.exports = {
|
|||||||
'<rootDir>/app/scripts/constants/error-utils.test.js',
|
'<rootDir>/app/scripts/constants/error-utils.test.js',
|
||||||
'<rootDir>/app/scripts/controllers/app-state.test.js',
|
'<rootDir>/app/scripts/controllers/app-state.test.js',
|
||||||
'<rootDir>/app/scripts/controllers/network/**/*.test.js',
|
'<rootDir>/app/scripts/controllers/network/**/*.test.js',
|
||||||
|
'<rootDir>/app/scripts/controllers/network/**/*.test.ts',
|
||||||
'<rootDir>/app/scripts/controllers/permissions/**/*.test.js',
|
'<rootDir>/app/scripts/controllers/permissions/**/*.test.js',
|
||||||
'<rootDir>/app/scripts/controllers/sign.test.ts',
|
'<rootDir>/app/scripts/controllers/sign.test.ts',
|
||||||
'<rootDir>/app/scripts/flask/**/*.test.js',
|
'<rootDir>/app/scripts/flask/**/*.test.js',
|
||||||
@ -51,7 +53,7 @@ module.exports = {
|
|||||||
'<rootDir>/ui/**/*.test.(js|ts|tsx)',
|
'<rootDir>/ui/**/*.test.(js|ts|tsx)',
|
||||||
'<rootDir>/development/fitness-functions/**/*.test.(js|ts|tsx)',
|
'<rootDir>/development/fitness-functions/**/*.test.(js|ts|tsx)',
|
||||||
],
|
],
|
||||||
testTimeout: 2500,
|
testTimeout: 5500,
|
||||||
// We have to specify the environment we are running in, which is jsdom. The
|
// We have to specify the environment we are running in, which is jsdom. The
|
||||||
// default is 'node'. This can be modified *per file* using a comment at the
|
// default is 'node'. This can be modified *per file* using a comment at the
|
||||||
// head of the file. So it may be worthwhile to switch to 'node' in any
|
// head of the file. So it may be worthwhile to switch to 'node' in any
|
||||||
|
@ -660,28 +660,8 @@
|
|||||||
},
|
},
|
||||||
"@metamask/address-book-controller": {
|
"@metamask/address-book-controller": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/address-book-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/address-book-controller>@metamask/controller-utils": true
|
"@metamask/controller-utils": true
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/address-book-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/address-book-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/announcement-controller": {
|
"@metamask/announcement-controller": {
|
||||||
@ -716,12 +696,12 @@
|
|||||||
"@ethersproject/contracts": true,
|
"@ethersproject/contracts": true,
|
||||||
"@ethersproject/providers": true,
|
"@ethersproject/providers": true,
|
||||||
"@metamask/assets-controllers>@metamask/abi-utils": true,
|
"@metamask/assets-controllers>@metamask/abi-utils": true,
|
||||||
"@metamask/assets-controllers>@metamask/controller-utils": true,
|
|
||||||
"@metamask/assets-controllers>@metamask/utils": true,
|
"@metamask/assets-controllers>@metamask/utils": true,
|
||||||
"@metamask/assets-controllers>abort-controller": true,
|
"@metamask/assets-controllers>abort-controller": true,
|
||||||
"@metamask/assets-controllers>multiformats": true,
|
"@metamask/assets-controllers>multiformats": true,
|
||||||
"@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/contract-metadata": true,
|
"@metamask/contract-metadata": true,
|
||||||
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/metamask-eth-abis": true,
|
"@metamask/metamask-eth-abis": true,
|
||||||
"browserify>events": true,
|
"browserify>events": true,
|
||||||
"eth-json-rpc-filters>async-mutex": true,
|
"eth-json-rpc-filters>async-mutex": true,
|
||||||
@ -750,21 +730,6 @@
|
|||||||
"semver": true
|
"semver": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/assets-controllers>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/assets-controllers>@metamask/utils": {
|
"@metamask/assets-controllers>@metamask/utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"TextDecoder": true,
|
"TextDecoder": true,
|
||||||
@ -1089,7 +1054,7 @@
|
|||||||
"@metamask/eth-token-tracker>deep-equal>is-date-object": true,
|
"@metamask/eth-token-tracker>deep-equal>is-date-object": true,
|
||||||
"@ngraveio/bc-ur>assert>object-is": true,
|
"@ngraveio/bc-ur>assert>object-is": true,
|
||||||
"@storybook/api>telejson>is-regex": true,
|
"@storybook/api>telejson>is-regex": true,
|
||||||
"mocha>object.assign>object-keys": true,
|
"globalthis>define-properties>object-keys": true,
|
||||||
"string.prototype.matchall>regexp.prototype.flags": true
|
"string.prototype.matchall>regexp.prototype.flags": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1193,34 +1158,14 @@
|
|||||||
"setInterval": true
|
"setInterval": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"eth-query": true,
|
"eth-query": true,
|
||||||
"ethereumjs-util": true,
|
"ethereumjs-util": true,
|
||||||
"ethjs>ethjs-unit": true,
|
"ethjs>ethjs-unit": true,
|
||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/jazzicon": {
|
"@metamask/jazzicon": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"document.createElement": true,
|
"document.createElement": true,
|
||||||
@ -1319,8 +1264,8 @@
|
|||||||
},
|
},
|
||||||
"@metamask/message-manager": {
|
"@metamask/message-manager": {
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@metamask/base-controller": true,
|
||||||
"@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/message-manager>@metamask/base-controller": true,
|
|
||||||
"@metamask/message-manager>jsonschema": true,
|
"@metamask/message-manager>jsonschema": true,
|
||||||
"browserify>buffer": true,
|
"browserify>buffer": true,
|
||||||
"browserify>events": true,
|
"browserify>events": true,
|
||||||
@ -1329,11 +1274,6 @@
|
|||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/message-manager>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/message-manager>jsonschema": {
|
"@metamask/message-manager>jsonschema": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"browserify>url": true
|
"browserify>url": true
|
||||||
@ -1364,8 +1304,8 @@
|
|||||||
"console.error": true
|
"console.error": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@metamask/base-controller": true,
|
||||||
"@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/permission-controller>@metamask/base-controller": true,
|
|
||||||
"@metamask/permission-controller>nanoid": true,
|
"@metamask/permission-controller>nanoid": true,
|
||||||
"deep-freeze-strict": true,
|
"deep-freeze-strict": true,
|
||||||
"eth-rpc-errors": true,
|
"eth-rpc-errors": true,
|
||||||
@ -1373,11 +1313,6 @@
|
|||||||
"json-rpc-engine": true
|
"json-rpc-engine": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/permission-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/permission-controller>nanoid": {
|
"@metamask/permission-controller>nanoid": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"crypto.getRandomValues": true
|
"crypto.getRandomValues": true
|
||||||
@ -1389,46 +1324,11 @@
|
|||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/phishing-controller>@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"@metamask/phishing-warning>eth-phishing-detect": true,
|
"@metamask/phishing-warning>eth-phishing-detect": true,
|
||||||
"punycode": true
|
"punycode": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/phishing-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": {
|
|
||||||
"globals": {
|
|
||||||
"fetch.bind": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch>whatwg-fetch": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch>whatwg-fetch": {
|
|
||||||
"globals": {
|
|
||||||
"Blob": true,
|
|
||||||
"FileReader": true,
|
|
||||||
"FormData": true,
|
|
||||||
"URLSearchParams.prototype.isPrototypeOf": true,
|
|
||||||
"XMLHttpRequest": true,
|
|
||||||
"define": true,
|
|
||||||
"setTimeout": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-warning>eth-phishing-detect": {
|
"@metamask/phishing-warning>eth-phishing-detect": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"eslint>optionator>fast-levenshtein": true
|
"eslint>optionator>fast-levenshtein": true
|
||||||
@ -1491,14 +1391,19 @@
|
|||||||
"@ethersproject/abi>@ethersproject/bytes": true,
|
"@ethersproject/abi>@ethersproject/bytes": true,
|
||||||
"@ethersproject/bignumber": true,
|
"@ethersproject/bignumber": true,
|
||||||
"@ethersproject/providers": true,
|
"@ethersproject/providers": true,
|
||||||
"@metamask/base-controller": true,
|
"@metamask/smart-transactions-controller>@metamask/base-controller": true,
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"@metamask/smart-transactions-controller>@metamask/controller-utils": true,
|
"@metamask/smart-transactions-controller>@metamask/controller-utils": true,
|
||||||
"@metamask/smart-transactions-controller>bignumber.js": true,
|
"@metamask/smart-transactions-controller>bignumber.js": true,
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch": true,
|
||||||
"fast-json-patch": true,
|
"fast-json-patch": true,
|
||||||
"lodash": true
|
"lodash": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@metamask/smart-transactions-controller>@metamask/base-controller": {
|
||||||
|
"packages": {
|
||||||
|
"immer": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"@metamask/smart-transactions-controller>@metamask/controller-utils": {
|
"@metamask/smart-transactions-controller>@metamask/controller-utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"console.error": true,
|
"console.error": true,
|
||||||
@ -1506,7 +1411,7 @@
|
|||||||
"setTimeout": true
|
"setTimeout": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
"@metamask/smart-transactions-controller>isomorphic-fetch": true,
|
||||||
"browserify>buffer": true,
|
"browserify>buffer": true,
|
||||||
"eslint>fast-deep-equal": true,
|
"eslint>fast-deep-equal": true,
|
||||||
"eth-ens-namehash": true,
|
"eth-ens-namehash": true,
|
||||||
@ -1525,6 +1430,25 @@
|
|||||||
"define": true
|
"define": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch": {
|
||||||
|
"globals": {
|
||||||
|
"fetch.bind": true
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch>whatwg-fetch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch>whatwg-fetch": {
|
||||||
|
"globals": {
|
||||||
|
"Blob": true,
|
||||||
|
"FileReader": true,
|
||||||
|
"FormData": true,
|
||||||
|
"URLSearchParams.prototype.isPrototypeOf": true,
|
||||||
|
"XMLHttpRequest": true,
|
||||||
|
"define": true,
|
||||||
|
"setTimeout": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"@metamask/snaps-controllers>nanoid": {
|
"@metamask/snaps-controllers>nanoid": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"crypto.getRandomValues": true
|
"crypto.getRandomValues": true
|
||||||
@ -2560,7 +2484,7 @@
|
|||||||
},
|
},
|
||||||
"browserify>has": {
|
"browserify>has": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>object.assign>function-bind": true
|
"browserify>has>function-bind": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browserify>os-browserify": {
|
"browserify>os-browserify": {
|
||||||
@ -3615,7 +3539,7 @@
|
|||||||
"globalthis>define-properties": {
|
"globalthis>define-properties": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"globalthis>define-properties>has-property-descriptors": true,
|
"globalthis>define-properties>has-property-descriptors": true,
|
||||||
"mocha>object.assign>object-keys": true
|
"globalthis>define-properties>object-keys": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"globalthis>define-properties>has-property-descriptors": {
|
"globalthis>define-properties>has-property-descriptors": {
|
||||||
@ -4235,7 +4159,7 @@
|
|||||||
},
|
},
|
||||||
"string.prototype.matchall>call-bind": {
|
"string.prototype.matchall>call-bind": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>object.assign>function-bind": true,
|
"browserify>has>function-bind": true,
|
||||||
"string.prototype.matchall>get-intrinsic": true
|
"string.prototype.matchall>get-intrinsic": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4247,7 +4171,7 @@
|
|||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"browserify>has": true,
|
"browserify>has": true,
|
||||||
"mocha>object.assign>function-bind": true,
|
"browserify>has>function-bind": true,
|
||||||
"string.prototype.matchall>has-symbols": true
|
"string.prototype.matchall>has-symbols": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -660,28 +660,8 @@
|
|||||||
},
|
},
|
||||||
"@metamask/address-book-controller": {
|
"@metamask/address-book-controller": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/address-book-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/address-book-controller>@metamask/controller-utils": true
|
"@metamask/controller-utils": true
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/address-book-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/address-book-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/announcement-controller": {
|
"@metamask/announcement-controller": {
|
||||||
@ -716,12 +696,12 @@
|
|||||||
"@ethersproject/contracts": true,
|
"@ethersproject/contracts": true,
|
||||||
"@ethersproject/providers": true,
|
"@ethersproject/providers": true,
|
||||||
"@metamask/assets-controllers>@metamask/abi-utils": true,
|
"@metamask/assets-controllers>@metamask/abi-utils": true,
|
||||||
"@metamask/assets-controllers>@metamask/controller-utils": true,
|
|
||||||
"@metamask/assets-controllers>@metamask/utils": true,
|
"@metamask/assets-controllers>@metamask/utils": true,
|
||||||
"@metamask/assets-controllers>abort-controller": true,
|
"@metamask/assets-controllers>abort-controller": true,
|
||||||
"@metamask/assets-controllers>multiformats": true,
|
"@metamask/assets-controllers>multiformats": true,
|
||||||
"@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/contract-metadata": true,
|
"@metamask/contract-metadata": true,
|
||||||
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/metamask-eth-abis": true,
|
"@metamask/metamask-eth-abis": true,
|
||||||
"browserify>events": true,
|
"browserify>events": true,
|
||||||
"eth-json-rpc-filters>async-mutex": true,
|
"eth-json-rpc-filters>async-mutex": true,
|
||||||
@ -750,21 +730,6 @@
|
|||||||
"semver": true
|
"semver": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/assets-controllers>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/assets-controllers>@metamask/utils": {
|
"@metamask/assets-controllers>@metamask/utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"TextDecoder": true,
|
"TextDecoder": true,
|
||||||
@ -1161,7 +1126,7 @@
|
|||||||
"@metamask/eth-token-tracker>deep-equal>is-date-object": true,
|
"@metamask/eth-token-tracker>deep-equal>is-date-object": true,
|
||||||
"@ngraveio/bc-ur>assert>object-is": true,
|
"@ngraveio/bc-ur>assert>object-is": true,
|
||||||
"@storybook/api>telejson>is-regex": true,
|
"@storybook/api>telejson>is-regex": true,
|
||||||
"mocha>object.assign>object-keys": true,
|
"globalthis>define-properties>object-keys": true,
|
||||||
"string.prototype.matchall>regexp.prototype.flags": true
|
"string.prototype.matchall>regexp.prototype.flags": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1265,34 +1230,14 @@
|
|||||||
"setInterval": true
|
"setInterval": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"eth-query": true,
|
"eth-query": true,
|
||||||
"ethereumjs-util": true,
|
"ethereumjs-util": true,
|
||||||
"ethjs>ethjs-unit": true,
|
"ethjs>ethjs-unit": true,
|
||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/jazzicon": {
|
"@metamask/jazzicon": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"document.createElement": true,
|
"document.createElement": true,
|
||||||
@ -1391,8 +1336,8 @@
|
|||||||
},
|
},
|
||||||
"@metamask/message-manager": {
|
"@metamask/message-manager": {
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@metamask/base-controller": true,
|
||||||
"@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/message-manager>@metamask/base-controller": true,
|
|
||||||
"@metamask/message-manager>jsonschema": true,
|
"@metamask/message-manager>jsonschema": true,
|
||||||
"browserify>buffer": true,
|
"browserify>buffer": true,
|
||||||
"browserify>events": true,
|
"browserify>events": true,
|
||||||
@ -1401,11 +1346,6 @@
|
|||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/message-manager>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/message-manager>jsonschema": {
|
"@metamask/message-manager>jsonschema": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"browserify>url": true
|
"browserify>url": true
|
||||||
@ -1413,31 +1353,11 @@
|
|||||||
},
|
},
|
||||||
"@metamask/notification-controller": {
|
"@metamask/notification-controller": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/notification-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/notification-controller>@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/notification-controller>nanoid": true
|
"@metamask/notification-controller>nanoid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/notification-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/notification-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/notification-controller>nanoid": {
|
"@metamask/notification-controller>nanoid": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"crypto.getRandomValues": true
|
"crypto.getRandomValues": true
|
||||||
@ -1463,8 +1383,8 @@
|
|||||||
"console.error": true
|
"console.error": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@metamask/base-controller": true,
|
||||||
"@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/permission-controller>@metamask/base-controller": true,
|
|
||||||
"@metamask/permission-controller>nanoid": true,
|
"@metamask/permission-controller>nanoid": true,
|
||||||
"deep-freeze-strict": true,
|
"deep-freeze-strict": true,
|
||||||
"eth-rpc-errors": true,
|
"eth-rpc-errors": true,
|
||||||
@ -1472,11 +1392,6 @@
|
|||||||
"json-rpc-engine": true
|
"json-rpc-engine": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/permission-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/permission-controller>nanoid": {
|
"@metamask/permission-controller>nanoid": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"crypto.getRandomValues": true
|
"crypto.getRandomValues": true
|
||||||
@ -1488,46 +1403,11 @@
|
|||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/phishing-controller>@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"@metamask/phishing-warning>eth-phishing-detect": true,
|
"@metamask/phishing-warning>eth-phishing-detect": true,
|
||||||
"punycode": true
|
"punycode": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/phishing-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": {
|
|
||||||
"globals": {
|
|
||||||
"fetch.bind": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch>whatwg-fetch": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch>whatwg-fetch": {
|
|
||||||
"globals": {
|
|
||||||
"Blob": true,
|
|
||||||
"FileReader": true,
|
|
||||||
"FormData": true,
|
|
||||||
"URLSearchParams.prototype.isPrototypeOf": true,
|
|
||||||
"XMLHttpRequest": true,
|
|
||||||
"define": true,
|
|
||||||
"setTimeout": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-warning>eth-phishing-detect": {
|
"@metamask/phishing-warning>eth-phishing-detect": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"eslint>optionator>fast-levenshtein": true
|
"eslint>optionator>fast-levenshtein": true
|
||||||
@ -1607,15 +1487,10 @@
|
|||||||
"setTimeout": true
|
"setTimeout": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/rate-limit-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"eth-rpc-errors": true
|
"eth-rpc-errors": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/rate-limit-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/rpc-methods": {
|
"@metamask/rpc-methods": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/key-tree": true,
|
"@metamask/key-tree": true,
|
||||||
@ -1679,14 +1554,19 @@
|
|||||||
"@ethersproject/abi>@ethersproject/bytes": true,
|
"@ethersproject/abi>@ethersproject/bytes": true,
|
||||||
"@ethersproject/bignumber": true,
|
"@ethersproject/bignumber": true,
|
||||||
"@ethersproject/providers": true,
|
"@ethersproject/providers": true,
|
||||||
"@metamask/base-controller": true,
|
"@metamask/smart-transactions-controller>@metamask/base-controller": true,
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"@metamask/smart-transactions-controller>@metamask/controller-utils": true,
|
"@metamask/smart-transactions-controller>@metamask/controller-utils": true,
|
||||||
"@metamask/smart-transactions-controller>bignumber.js": true,
|
"@metamask/smart-transactions-controller>bignumber.js": true,
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch": true,
|
||||||
"fast-json-patch": true,
|
"fast-json-patch": true,
|
||||||
"lodash": true
|
"lodash": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@metamask/smart-transactions-controller>@metamask/base-controller": {
|
||||||
|
"packages": {
|
||||||
|
"immer": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"@metamask/smart-transactions-controller>@metamask/controller-utils": {
|
"@metamask/smart-transactions-controller>@metamask/controller-utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"console.error": true,
|
"console.error": true,
|
||||||
@ -1694,7 +1574,7 @@
|
|||||||
"setTimeout": true
|
"setTimeout": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
"@metamask/smart-transactions-controller>isomorphic-fetch": true,
|
||||||
"browserify>buffer": true,
|
"browserify>buffer": true,
|
||||||
"eslint>fast-deep-equal": true,
|
"eslint>fast-deep-equal": true,
|
||||||
"eth-ens-namehash": true,
|
"eth-ens-namehash": true,
|
||||||
@ -1713,6 +1593,25 @@
|
|||||||
"define": true
|
"define": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch": {
|
||||||
|
"globals": {
|
||||||
|
"fetch.bind": true
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch>whatwg-fetch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch>whatwg-fetch": {
|
||||||
|
"globals": {
|
||||||
|
"Blob": true,
|
||||||
|
"FileReader": true,
|
||||||
|
"FormData": true,
|
||||||
|
"URLSearchParams.prototype.isPrototypeOf": true,
|
||||||
|
"XMLHttpRequest": true,
|
||||||
|
"define": true,
|
||||||
|
"setTimeout": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"@metamask/snaps-controllers": {
|
"@metamask/snaps-controllers": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"URL": true,
|
"URL": true,
|
||||||
@ -1724,12 +1623,11 @@
|
|||||||
"setTimeout": true
|
"setTimeout": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@metamask/base-controller": true,
|
||||||
"@metamask/permission-controller": true,
|
"@metamask/permission-controller": true,
|
||||||
"@metamask/post-message-stream": true,
|
"@metamask/post-message-stream": true,
|
||||||
"@metamask/providers>@metamask/object-multiplex": true,
|
"@metamask/providers>@metamask/object-multiplex": true,
|
||||||
"@metamask/rpc-methods": true,
|
"@metamask/rpc-methods": true,
|
||||||
"@metamask/snaps-controllers>@metamask/base-controller": true,
|
|
||||||
"@metamask/snaps-controllers>@metamask/subject-metadata-controller": true,
|
|
||||||
"@metamask/snaps-controllers>@xstate/fsm": true,
|
"@metamask/snaps-controllers>@xstate/fsm": true,
|
||||||
"@metamask/snaps-controllers>concat-stream": true,
|
"@metamask/snaps-controllers>concat-stream": true,
|
||||||
"@metamask/snaps-controllers>gunzip-maybe": true,
|
"@metamask/snaps-controllers>gunzip-maybe": true,
|
||||||
@ -1738,6 +1636,7 @@
|
|||||||
"@metamask/snaps-controllers>tar-stream": true,
|
"@metamask/snaps-controllers>tar-stream": true,
|
||||||
"@metamask/snaps-utils": true,
|
"@metamask/snaps-utils": true,
|
||||||
"@metamask/snaps-utils>@metamask/snaps-registry": true,
|
"@metamask/snaps-utils>@metamask/snaps-registry": true,
|
||||||
|
"@metamask/subject-metadata-controller": true,
|
||||||
"@metamask/utils": true,
|
"@metamask/utils": true,
|
||||||
"eth-rpc-errors": true,
|
"eth-rpc-errors": true,
|
||||||
"json-rpc-engine": true,
|
"json-rpc-engine": true,
|
||||||
@ -1745,16 +1644,6 @@
|
|||||||
"pump": true
|
"pump": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/snaps-controllers>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/snaps-controllers>@metamask/subject-metadata-controller": {
|
|
||||||
"packages": {
|
|
||||||
"@metamask/snaps-controllers>@metamask/base-controller": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/snaps-controllers>concat-stream": {
|
"@metamask/snaps-controllers>concat-stream": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/snaps-controllers>concat-stream>readable-stream": true,
|
"@metamask/snaps-controllers>concat-stream>readable-stream": true,
|
||||||
@ -2988,7 +2877,7 @@
|
|||||||
},
|
},
|
||||||
"browserify>has": {
|
"browserify>has": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>object.assign>function-bind": true
|
"browserify>has>function-bind": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browserify>os-browserify": {
|
"browserify>os-browserify": {
|
||||||
@ -4043,7 +3932,7 @@
|
|||||||
"globalthis>define-properties": {
|
"globalthis>define-properties": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"globalthis>define-properties>has-property-descriptors": true,
|
"globalthis>define-properties>has-property-descriptors": true,
|
||||||
"mocha>object.assign>object-keys": true
|
"globalthis>define-properties>object-keys": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"globalthis>define-properties>has-property-descriptors": {
|
"globalthis>define-properties>has-property-descriptors": {
|
||||||
@ -4457,9 +4346,9 @@
|
|||||||
"react-markdown>unified": {
|
"react-markdown>unified": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"jsdom>request>extend": true,
|
"jsdom>request>extend": true,
|
||||||
|
"mocha>yargs-unparser>is-plain-obj": true,
|
||||||
"react-markdown>unified>bail": true,
|
"react-markdown>unified>bail": true,
|
||||||
"react-markdown>unified>is-buffer": true,
|
"react-markdown>unified>is-buffer": true,
|
||||||
"react-markdown>unified>is-plain-obj": true,
|
|
||||||
"react-markdown>unified>trough": true,
|
"react-markdown>unified>trough": true,
|
||||||
"react-markdown>vfile": true
|
"react-markdown>vfile": true
|
||||||
}
|
}
|
||||||
@ -4795,7 +4684,7 @@
|
|||||||
},
|
},
|
||||||
"string.prototype.matchall>call-bind": {
|
"string.prototype.matchall>call-bind": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>object.assign>function-bind": true,
|
"browserify>has>function-bind": true,
|
||||||
"string.prototype.matchall>get-intrinsic": true
|
"string.prototype.matchall>get-intrinsic": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4807,7 +4696,7 @@
|
|||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"browserify>has": true,
|
"browserify>has": true,
|
||||||
"mocha>object.assign>function-bind": true,
|
"browserify>has>function-bind": true,
|
||||||
"string.prototype.matchall>has-symbols": true
|
"string.prototype.matchall>has-symbols": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -660,28 +660,8 @@
|
|||||||
},
|
},
|
||||||
"@metamask/address-book-controller": {
|
"@metamask/address-book-controller": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/address-book-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/address-book-controller>@metamask/controller-utils": true
|
"@metamask/controller-utils": true
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/address-book-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/address-book-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/announcement-controller": {
|
"@metamask/announcement-controller": {
|
||||||
@ -716,12 +696,12 @@
|
|||||||
"@ethersproject/contracts": true,
|
"@ethersproject/contracts": true,
|
||||||
"@ethersproject/providers": true,
|
"@ethersproject/providers": true,
|
||||||
"@metamask/assets-controllers>@metamask/abi-utils": true,
|
"@metamask/assets-controllers>@metamask/abi-utils": true,
|
||||||
"@metamask/assets-controllers>@metamask/controller-utils": true,
|
|
||||||
"@metamask/assets-controllers>@metamask/utils": true,
|
"@metamask/assets-controllers>@metamask/utils": true,
|
||||||
"@metamask/assets-controllers>abort-controller": true,
|
"@metamask/assets-controllers>abort-controller": true,
|
||||||
"@metamask/assets-controllers>multiformats": true,
|
"@metamask/assets-controllers>multiformats": true,
|
||||||
"@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/contract-metadata": true,
|
"@metamask/contract-metadata": true,
|
||||||
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/metamask-eth-abis": true,
|
"@metamask/metamask-eth-abis": true,
|
||||||
"browserify>events": true,
|
"browserify>events": true,
|
||||||
"eth-json-rpc-filters>async-mutex": true,
|
"eth-json-rpc-filters>async-mutex": true,
|
||||||
@ -750,21 +730,6 @@
|
|||||||
"semver": true
|
"semver": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/assets-controllers>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/assets-controllers>@metamask/utils": {
|
"@metamask/assets-controllers>@metamask/utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"TextDecoder": true,
|
"TextDecoder": true,
|
||||||
@ -1161,7 +1126,7 @@
|
|||||||
"@metamask/eth-token-tracker>deep-equal>is-date-object": true,
|
"@metamask/eth-token-tracker>deep-equal>is-date-object": true,
|
||||||
"@ngraveio/bc-ur>assert>object-is": true,
|
"@ngraveio/bc-ur>assert>object-is": true,
|
||||||
"@storybook/api>telejson>is-regex": true,
|
"@storybook/api>telejson>is-regex": true,
|
||||||
"mocha>object.assign>object-keys": true,
|
"globalthis>define-properties>object-keys": true,
|
||||||
"string.prototype.matchall>regexp.prototype.flags": true
|
"string.prototype.matchall>regexp.prototype.flags": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1265,34 +1230,14 @@
|
|||||||
"setInterval": true
|
"setInterval": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"eth-query": true,
|
"eth-query": true,
|
||||||
"ethereumjs-util": true,
|
"ethereumjs-util": true,
|
||||||
"ethjs>ethjs-unit": true,
|
"ethjs>ethjs-unit": true,
|
||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/jazzicon": {
|
"@metamask/jazzicon": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"document.createElement": true,
|
"document.createElement": true,
|
||||||
@ -1391,8 +1336,8 @@
|
|||||||
},
|
},
|
||||||
"@metamask/message-manager": {
|
"@metamask/message-manager": {
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@metamask/base-controller": true,
|
||||||
"@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/message-manager>@metamask/base-controller": true,
|
|
||||||
"@metamask/message-manager>jsonschema": true,
|
"@metamask/message-manager>jsonschema": true,
|
||||||
"browserify>buffer": true,
|
"browserify>buffer": true,
|
||||||
"browserify>events": true,
|
"browserify>events": true,
|
||||||
@ -1401,11 +1346,6 @@
|
|||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/message-manager>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/message-manager>jsonschema": {
|
"@metamask/message-manager>jsonschema": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"browserify>url": true
|
"browserify>url": true
|
||||||
@ -1413,31 +1353,11 @@
|
|||||||
},
|
},
|
||||||
"@metamask/notification-controller": {
|
"@metamask/notification-controller": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/notification-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/notification-controller>@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/notification-controller>nanoid": true
|
"@metamask/notification-controller>nanoid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/notification-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/notification-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/notification-controller>nanoid": {
|
"@metamask/notification-controller>nanoid": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"crypto.getRandomValues": true
|
"crypto.getRandomValues": true
|
||||||
@ -1463,8 +1383,8 @@
|
|||||||
"console.error": true
|
"console.error": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@metamask/base-controller": true,
|
||||||
"@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/permission-controller>@metamask/base-controller": true,
|
|
||||||
"@metamask/permission-controller>nanoid": true,
|
"@metamask/permission-controller>nanoid": true,
|
||||||
"deep-freeze-strict": true,
|
"deep-freeze-strict": true,
|
||||||
"eth-rpc-errors": true,
|
"eth-rpc-errors": true,
|
||||||
@ -1472,11 +1392,6 @@
|
|||||||
"json-rpc-engine": true
|
"json-rpc-engine": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/permission-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/permission-controller>nanoid": {
|
"@metamask/permission-controller>nanoid": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"crypto.getRandomValues": true
|
"crypto.getRandomValues": true
|
||||||
@ -1488,46 +1403,11 @@
|
|||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/phishing-controller>@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"@metamask/phishing-warning>eth-phishing-detect": true,
|
"@metamask/phishing-warning>eth-phishing-detect": true,
|
||||||
"punycode": true
|
"punycode": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/phishing-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": {
|
|
||||||
"globals": {
|
|
||||||
"fetch.bind": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch>whatwg-fetch": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch>whatwg-fetch": {
|
|
||||||
"globals": {
|
|
||||||
"Blob": true,
|
|
||||||
"FileReader": true,
|
|
||||||
"FormData": true,
|
|
||||||
"URLSearchParams.prototype.isPrototypeOf": true,
|
|
||||||
"XMLHttpRequest": true,
|
|
||||||
"define": true,
|
|
||||||
"setTimeout": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-warning>eth-phishing-detect": {
|
"@metamask/phishing-warning>eth-phishing-detect": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"eslint>optionator>fast-levenshtein": true
|
"eslint>optionator>fast-levenshtein": true
|
||||||
@ -1607,15 +1487,10 @@
|
|||||||
"setTimeout": true
|
"setTimeout": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/rate-limit-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"eth-rpc-errors": true
|
"eth-rpc-errors": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/rate-limit-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/rpc-methods": {
|
"@metamask/rpc-methods": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/key-tree": true,
|
"@metamask/key-tree": true,
|
||||||
@ -1679,14 +1554,19 @@
|
|||||||
"@ethersproject/abi>@ethersproject/bytes": true,
|
"@ethersproject/abi>@ethersproject/bytes": true,
|
||||||
"@ethersproject/bignumber": true,
|
"@ethersproject/bignumber": true,
|
||||||
"@ethersproject/providers": true,
|
"@ethersproject/providers": true,
|
||||||
"@metamask/base-controller": true,
|
"@metamask/smart-transactions-controller>@metamask/base-controller": true,
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"@metamask/smart-transactions-controller>@metamask/controller-utils": true,
|
"@metamask/smart-transactions-controller>@metamask/controller-utils": true,
|
||||||
"@metamask/smart-transactions-controller>bignumber.js": true,
|
"@metamask/smart-transactions-controller>bignumber.js": true,
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch": true,
|
||||||
"fast-json-patch": true,
|
"fast-json-patch": true,
|
||||||
"lodash": true
|
"lodash": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@metamask/smart-transactions-controller>@metamask/base-controller": {
|
||||||
|
"packages": {
|
||||||
|
"immer": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"@metamask/smart-transactions-controller>@metamask/controller-utils": {
|
"@metamask/smart-transactions-controller>@metamask/controller-utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"console.error": true,
|
"console.error": true,
|
||||||
@ -1694,7 +1574,7 @@
|
|||||||
"setTimeout": true
|
"setTimeout": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
"@metamask/smart-transactions-controller>isomorphic-fetch": true,
|
||||||
"browserify>buffer": true,
|
"browserify>buffer": true,
|
||||||
"eslint>fast-deep-equal": true,
|
"eslint>fast-deep-equal": true,
|
||||||
"eth-ens-namehash": true,
|
"eth-ens-namehash": true,
|
||||||
@ -1713,6 +1593,25 @@
|
|||||||
"define": true
|
"define": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch": {
|
||||||
|
"globals": {
|
||||||
|
"fetch.bind": true
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch>whatwg-fetch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch>whatwg-fetch": {
|
||||||
|
"globals": {
|
||||||
|
"Blob": true,
|
||||||
|
"FileReader": true,
|
||||||
|
"FormData": true,
|
||||||
|
"URLSearchParams.prototype.isPrototypeOf": true,
|
||||||
|
"XMLHttpRequest": true,
|
||||||
|
"define": true,
|
||||||
|
"setTimeout": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"@metamask/snaps-controllers": {
|
"@metamask/snaps-controllers": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"URL": true,
|
"URL": true,
|
||||||
@ -1724,12 +1623,11 @@
|
|||||||
"setTimeout": true
|
"setTimeout": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@metamask/base-controller": true,
|
||||||
"@metamask/permission-controller": true,
|
"@metamask/permission-controller": true,
|
||||||
"@metamask/post-message-stream": true,
|
"@metamask/post-message-stream": true,
|
||||||
"@metamask/providers>@metamask/object-multiplex": true,
|
"@metamask/providers>@metamask/object-multiplex": true,
|
||||||
"@metamask/rpc-methods": true,
|
"@metamask/rpc-methods": true,
|
||||||
"@metamask/snaps-controllers>@metamask/base-controller": true,
|
|
||||||
"@metamask/snaps-controllers>@metamask/subject-metadata-controller": true,
|
|
||||||
"@metamask/snaps-controllers>@xstate/fsm": true,
|
"@metamask/snaps-controllers>@xstate/fsm": true,
|
||||||
"@metamask/snaps-controllers>concat-stream": true,
|
"@metamask/snaps-controllers>concat-stream": true,
|
||||||
"@metamask/snaps-controllers>gunzip-maybe": true,
|
"@metamask/snaps-controllers>gunzip-maybe": true,
|
||||||
@ -1738,6 +1636,7 @@
|
|||||||
"@metamask/snaps-controllers>tar-stream": true,
|
"@metamask/snaps-controllers>tar-stream": true,
|
||||||
"@metamask/snaps-utils": true,
|
"@metamask/snaps-utils": true,
|
||||||
"@metamask/snaps-utils>@metamask/snaps-registry": true,
|
"@metamask/snaps-utils>@metamask/snaps-registry": true,
|
||||||
|
"@metamask/subject-metadata-controller": true,
|
||||||
"@metamask/utils": true,
|
"@metamask/utils": true,
|
||||||
"eth-rpc-errors": true,
|
"eth-rpc-errors": true,
|
||||||
"json-rpc-engine": true,
|
"json-rpc-engine": true,
|
||||||
@ -1745,16 +1644,6 @@
|
|||||||
"pump": true
|
"pump": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/snaps-controllers>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/snaps-controllers>@metamask/subject-metadata-controller": {
|
|
||||||
"packages": {
|
|
||||||
"@metamask/snaps-controllers>@metamask/base-controller": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/snaps-controllers>concat-stream": {
|
"@metamask/snaps-controllers>concat-stream": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/snaps-controllers>concat-stream>readable-stream": true,
|
"@metamask/snaps-controllers>concat-stream>readable-stream": true,
|
||||||
@ -2988,7 +2877,7 @@
|
|||||||
},
|
},
|
||||||
"browserify>has": {
|
"browserify>has": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>object.assign>function-bind": true
|
"browserify>has>function-bind": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browserify>os-browserify": {
|
"browserify>os-browserify": {
|
||||||
@ -4043,7 +3932,7 @@
|
|||||||
"globalthis>define-properties": {
|
"globalthis>define-properties": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"globalthis>define-properties>has-property-descriptors": true,
|
"globalthis>define-properties>has-property-descriptors": true,
|
||||||
"mocha>object.assign>object-keys": true
|
"globalthis>define-properties>object-keys": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"globalthis>define-properties>has-property-descriptors": {
|
"globalthis>define-properties>has-property-descriptors": {
|
||||||
@ -4457,9 +4346,9 @@
|
|||||||
"react-markdown>unified": {
|
"react-markdown>unified": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"jsdom>request>extend": true,
|
"jsdom>request>extend": true,
|
||||||
|
"mocha>yargs-unparser>is-plain-obj": true,
|
||||||
"react-markdown>unified>bail": true,
|
"react-markdown>unified>bail": true,
|
||||||
"react-markdown>unified>is-buffer": true,
|
"react-markdown>unified>is-buffer": true,
|
||||||
"react-markdown>unified>is-plain-obj": true,
|
|
||||||
"react-markdown>unified>trough": true,
|
"react-markdown>unified>trough": true,
|
||||||
"react-markdown>vfile": true
|
"react-markdown>vfile": true
|
||||||
}
|
}
|
||||||
@ -4795,7 +4684,7 @@
|
|||||||
},
|
},
|
||||||
"string.prototype.matchall>call-bind": {
|
"string.prototype.matchall>call-bind": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>object.assign>function-bind": true,
|
"browserify>has>function-bind": true,
|
||||||
"string.prototype.matchall>get-intrinsic": true
|
"string.prototype.matchall>get-intrinsic": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4807,7 +4696,7 @@
|
|||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"browserify>has": true,
|
"browserify>has": true,
|
||||||
"mocha>object.assign>function-bind": true,
|
"browserify>has>function-bind": true,
|
||||||
"string.prototype.matchall>has-symbols": true
|
"string.prototype.matchall>has-symbols": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -660,28 +660,8 @@
|
|||||||
},
|
},
|
||||||
"@metamask/address-book-controller": {
|
"@metamask/address-book-controller": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/address-book-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/address-book-controller>@metamask/controller-utils": true
|
"@metamask/controller-utils": true
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/address-book-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/address-book-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/announcement-controller": {
|
"@metamask/announcement-controller": {
|
||||||
@ -716,12 +696,12 @@
|
|||||||
"@ethersproject/contracts": true,
|
"@ethersproject/contracts": true,
|
||||||
"@ethersproject/providers": true,
|
"@ethersproject/providers": true,
|
||||||
"@metamask/assets-controllers>@metamask/abi-utils": true,
|
"@metamask/assets-controllers>@metamask/abi-utils": true,
|
||||||
"@metamask/assets-controllers>@metamask/controller-utils": true,
|
|
||||||
"@metamask/assets-controllers>@metamask/utils": true,
|
"@metamask/assets-controllers>@metamask/utils": true,
|
||||||
"@metamask/assets-controllers>abort-controller": true,
|
"@metamask/assets-controllers>abort-controller": true,
|
||||||
"@metamask/assets-controllers>multiformats": true,
|
"@metamask/assets-controllers>multiformats": true,
|
||||||
"@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/contract-metadata": true,
|
"@metamask/contract-metadata": true,
|
||||||
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/metamask-eth-abis": true,
|
"@metamask/metamask-eth-abis": true,
|
||||||
"browserify>events": true,
|
"browserify>events": true,
|
||||||
"eth-json-rpc-filters>async-mutex": true,
|
"eth-json-rpc-filters>async-mutex": true,
|
||||||
@ -750,21 +730,6 @@
|
|||||||
"semver": true
|
"semver": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/assets-controllers>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/assets-controllers>@metamask/utils": {
|
"@metamask/assets-controllers>@metamask/utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"TextDecoder": true,
|
"TextDecoder": true,
|
||||||
@ -1089,7 +1054,7 @@
|
|||||||
"@metamask/eth-token-tracker>deep-equal>is-date-object": true,
|
"@metamask/eth-token-tracker>deep-equal>is-date-object": true,
|
||||||
"@ngraveio/bc-ur>assert>object-is": true,
|
"@ngraveio/bc-ur>assert>object-is": true,
|
||||||
"@storybook/api>telejson>is-regex": true,
|
"@storybook/api>telejson>is-regex": true,
|
||||||
"mocha>object.assign>object-keys": true,
|
"globalthis>define-properties>object-keys": true,
|
||||||
"string.prototype.matchall>regexp.prototype.flags": true
|
"string.prototype.matchall>regexp.prototype.flags": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1193,34 +1158,14 @@
|
|||||||
"setInterval": true
|
"setInterval": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"eth-query": true,
|
"eth-query": true,
|
||||||
"ethereumjs-util": true,
|
"ethereumjs-util": true,
|
||||||
"ethjs>ethjs-unit": true,
|
"ethjs>ethjs-unit": true,
|
||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/jazzicon": {
|
"@metamask/jazzicon": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"document.createElement": true,
|
"document.createElement": true,
|
||||||
@ -1319,8 +1264,8 @@
|
|||||||
},
|
},
|
||||||
"@metamask/message-manager": {
|
"@metamask/message-manager": {
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@metamask/base-controller": true,
|
||||||
"@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/message-manager>@metamask/base-controller": true,
|
|
||||||
"@metamask/message-manager>jsonschema": true,
|
"@metamask/message-manager>jsonschema": true,
|
||||||
"browserify>buffer": true,
|
"browserify>buffer": true,
|
||||||
"browserify>events": true,
|
"browserify>events": true,
|
||||||
@ -1329,11 +1274,6 @@
|
|||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/message-manager>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/message-manager>jsonschema": {
|
"@metamask/message-manager>jsonschema": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"browserify>url": true
|
"browserify>url": true
|
||||||
@ -1364,8 +1304,8 @@
|
|||||||
"console.error": true
|
"console.error": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@metamask/base-controller": true,
|
||||||
"@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/permission-controller>@metamask/base-controller": true,
|
|
||||||
"@metamask/permission-controller>nanoid": true,
|
"@metamask/permission-controller>nanoid": true,
|
||||||
"deep-freeze-strict": true,
|
"deep-freeze-strict": true,
|
||||||
"eth-rpc-errors": true,
|
"eth-rpc-errors": true,
|
||||||
@ -1373,11 +1313,6 @@
|
|||||||
"json-rpc-engine": true
|
"json-rpc-engine": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/permission-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/permission-controller>nanoid": {
|
"@metamask/permission-controller>nanoid": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"crypto.getRandomValues": true
|
"crypto.getRandomValues": true
|
||||||
@ -1389,46 +1324,11 @@
|
|||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/base-controller": true,
|
"@metamask/base-controller": true,
|
||||||
"@metamask/phishing-controller>@metamask/controller-utils": true,
|
"@metamask/controller-utils": true,
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"@metamask/phishing-warning>eth-phishing-detect": true,
|
"@metamask/phishing-warning>eth-phishing-detect": true,
|
||||||
"punycode": true
|
"punycode": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/phishing-controller>@metamask/controller-utils": {
|
|
||||||
"globals": {
|
|
||||||
"console.error": true,
|
|
||||||
"fetch": true,
|
|
||||||
"setTimeout": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"browserify>buffer": true,
|
|
||||||
"eslint>fast-deep-equal": true,
|
|
||||||
"eth-ens-namehash": true,
|
|
||||||
"ethereumjs-util": true,
|
|
||||||
"ethjs>ethjs-unit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": {
|
|
||||||
"globals": {
|
|
||||||
"fetch.bind": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch>whatwg-fetch": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-controller>isomorphic-fetch>whatwg-fetch": {
|
|
||||||
"globals": {
|
|
||||||
"Blob": true,
|
|
||||||
"FileReader": true,
|
|
||||||
"FormData": true,
|
|
||||||
"URLSearchParams.prototype.isPrototypeOf": true,
|
|
||||||
"XMLHttpRequest": true,
|
|
||||||
"define": true,
|
|
||||||
"setTimeout": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/phishing-warning>eth-phishing-detect": {
|
"@metamask/phishing-warning>eth-phishing-detect": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"eslint>optionator>fast-levenshtein": true
|
"eslint>optionator>fast-levenshtein": true
|
||||||
@ -1491,14 +1391,19 @@
|
|||||||
"@ethersproject/abi>@ethersproject/bytes": true,
|
"@ethersproject/abi>@ethersproject/bytes": true,
|
||||||
"@ethersproject/bignumber": true,
|
"@ethersproject/bignumber": true,
|
||||||
"@ethersproject/providers": true,
|
"@ethersproject/providers": true,
|
||||||
"@metamask/base-controller": true,
|
"@metamask/smart-transactions-controller>@metamask/base-controller": true,
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
|
||||||
"@metamask/smart-transactions-controller>@metamask/controller-utils": true,
|
"@metamask/smart-transactions-controller>@metamask/controller-utils": true,
|
||||||
"@metamask/smart-transactions-controller>bignumber.js": true,
|
"@metamask/smart-transactions-controller>bignumber.js": true,
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch": true,
|
||||||
"fast-json-patch": true,
|
"fast-json-patch": true,
|
||||||
"lodash": true
|
"lodash": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@metamask/smart-transactions-controller>@metamask/base-controller": {
|
||||||
|
"packages": {
|
||||||
|
"immer": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"@metamask/smart-transactions-controller>@metamask/controller-utils": {
|
"@metamask/smart-transactions-controller>@metamask/controller-utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"console.error": true,
|
"console.error": true,
|
||||||
@ -1506,7 +1411,7 @@
|
|||||||
"setTimeout": true
|
"setTimeout": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@metamask/phishing-controller>isomorphic-fetch": true,
|
"@metamask/smart-transactions-controller>isomorphic-fetch": true,
|
||||||
"browserify>buffer": true,
|
"browserify>buffer": true,
|
||||||
"eslint>fast-deep-equal": true,
|
"eslint>fast-deep-equal": true,
|
||||||
"eth-ens-namehash": true,
|
"eth-ens-namehash": true,
|
||||||
@ -1525,6 +1430,25 @@
|
|||||||
"define": true
|
"define": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch": {
|
||||||
|
"globals": {
|
||||||
|
"fetch.bind": true
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch>whatwg-fetch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@metamask/smart-transactions-controller>isomorphic-fetch>whatwg-fetch": {
|
||||||
|
"globals": {
|
||||||
|
"Blob": true,
|
||||||
|
"FileReader": true,
|
||||||
|
"FormData": true,
|
||||||
|
"URLSearchParams.prototype.isPrototypeOf": true,
|
||||||
|
"XMLHttpRequest": true,
|
||||||
|
"define": true,
|
||||||
|
"setTimeout": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"@metamask/snaps-controllers>nanoid": {
|
"@metamask/snaps-controllers>nanoid": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"crypto.getRandomValues": true
|
"crypto.getRandomValues": true
|
||||||
@ -2560,7 +2484,7 @@
|
|||||||
},
|
},
|
||||||
"browserify>has": {
|
"browserify>has": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>object.assign>function-bind": true
|
"browserify>has>function-bind": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browserify>os-browserify": {
|
"browserify>os-browserify": {
|
||||||
@ -3615,7 +3539,7 @@
|
|||||||
"globalthis>define-properties": {
|
"globalthis>define-properties": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"globalthis>define-properties>has-property-descriptors": true,
|
"globalthis>define-properties>has-property-descriptors": true,
|
||||||
"mocha>object.assign>object-keys": true
|
"globalthis>define-properties>object-keys": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"globalthis>define-properties>has-property-descriptors": {
|
"globalthis>define-properties>has-property-descriptors": {
|
||||||
@ -4235,7 +4159,7 @@
|
|||||||
},
|
},
|
||||||
"string.prototype.matchall>call-bind": {
|
"string.prototype.matchall>call-bind": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>object.assign>function-bind": true,
|
"browserify>has>function-bind": true,
|
||||||
"string.prototype.matchall>get-intrinsic": true
|
"string.prototype.matchall>get-intrinsic": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4247,7 +4171,7 @@
|
|||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"browserify>has": true,
|
"browserify>has": true,
|
||||||
"mocha>object.assign>function-bind": true,
|
"browserify>has>function-bind": true,
|
||||||
"string.prototype.matchall>has-symbols": true
|
"string.prototype.matchall>has-symbols": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1697,7 +1697,7 @@
|
|||||||
},
|
},
|
||||||
"browserify>has": {
|
"browserify>has": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>object.assign>function-bind": true
|
"browserify>has>function-bind": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browserify>insert-module-globals": {
|
"browserify>insert-module-globals": {
|
||||||
@ -1885,12 +1885,7 @@
|
|||||||
"process.platform": true
|
"process.platform": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"chalk>supports-color>has-flag": true
|
"sinon>supports-color>has-flag": true
|
||||||
}
|
|
||||||
},
|
|
||||||
"chalk>supports-color>has-flag": {
|
|
||||||
"globals": {
|
|
||||||
"process.argv": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chokidar": {
|
"chokidar": {
|
||||||
@ -2002,7 +1997,7 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"cross-spawn>path-key": true,
|
"cross-spawn>path-key": true,
|
||||||
"cross-spawn>shebang-command": true,
|
"cross-spawn>shebang-command": true,
|
||||||
"cross-spawn>which": true
|
"mocha>which": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cross-spawn>path-key": {
|
"cross-spawn>path-key": {
|
||||||
@ -2016,21 +2011,6 @@
|
|||||||
"cross-spawn>shebang-command>shebang-regex": true
|
"cross-spawn>shebang-command>shebang-regex": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cross-spawn>which": {
|
|
||||||
"builtin": {
|
|
||||||
"path.join": true
|
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
"process.cwd": true,
|
|
||||||
"process.env.OSTYPE": true,
|
|
||||||
"process.env.PATH": true,
|
|
||||||
"process.env.PATHEXT": true,
|
|
||||||
"process.platform": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"mocha>which>isexe": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"debounce-stream>duplexer": {
|
"debounce-stream>duplexer": {
|
||||||
"builtin": {
|
"builtin": {
|
||||||
"stream": true
|
"stream": true
|
||||||
@ -2350,7 +2330,7 @@
|
|||||||
"process": true
|
"process": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"gulp-livereload>debug>ms": true,
|
"mocha>ms": true,
|
||||||
"mocha>supports-color": true
|
"mocha>supports-color": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2471,7 +2451,7 @@
|
|||||||
"process": true
|
"process": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"gulp-livereload>debug>ms": true,
|
"mocha>ms": true,
|
||||||
"mocha>supports-color": true
|
"mocha>supports-color": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2871,8 +2851,8 @@
|
|||||||
"eslint>@eslint/eslintrc>globals": true,
|
"eslint>@eslint/eslintrc>globals": true,
|
||||||
"eslint>ajv": true,
|
"eslint>ajv": true,
|
||||||
"eslint>minimatch": true,
|
"eslint>minimatch": true,
|
||||||
"eslint>strip-json-comments": true,
|
|
||||||
"globby>ignore": true,
|
"globby>ignore": true,
|
||||||
|
"mocha>strip-json-comments": true,
|
||||||
"nock>debug": true
|
"nock>debug": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3094,9 +3074,9 @@
|
|||||||
"process.cwd": true
|
"process.cwd": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"chokidar>glob-parent": true,
|
|
||||||
"fast-glob>@nodelib/fs.stat": true,
|
"fast-glob>@nodelib/fs.stat": true,
|
||||||
"fast-glob>@nodelib/fs.walk": true,
|
"fast-glob>@nodelib/fs.walk": true,
|
||||||
|
"fast-glob>glob-parent": true,
|
||||||
"globby>merge2": true,
|
"globby>merge2": true,
|
||||||
"stylelint>micromatch": true
|
"stylelint>micromatch": true
|
||||||
}
|
}
|
||||||
@ -3151,6 +3131,15 @@
|
|||||||
"fast-glob>@nodelib/fs.walk>fastq>reusify": true
|
"fast-glob>@nodelib/fs.walk>fastq>reusify": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fast-glob>glob-parent": {
|
||||||
|
"builtin": {
|
||||||
|
"os.platform": true,
|
||||||
|
"path.posix.dirname": true
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"eslint>is-glob": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"builtin": {
|
"builtin": {
|
||||||
"assert": true,
|
"assert": true,
|
||||||
@ -3200,7 +3189,7 @@
|
|||||||
"globalthis>define-properties": {
|
"globalthis>define-properties": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"globalthis>define-properties>has-property-descriptors": true,
|
"globalthis>define-properties>has-property-descriptors": true,
|
||||||
"mocha>object.assign>object-keys": true
|
"globalthis>define-properties>object-keys": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"globalthis>define-properties>has-property-descriptors": {
|
"globalthis>define-properties>has-property-descriptors": {
|
||||||
@ -3370,8 +3359,8 @@
|
|||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"gulp-dart-sass>chalk>ansi-styles": true,
|
"gulp-dart-sass>chalk>ansi-styles": true,
|
||||||
"gulp-dart-sass>chalk>supports-color": true,
|
"gulp-dart-sass>chalk>escape-string-regexp": true,
|
||||||
"mocha>escape-string-regexp": true
|
"gulp-dart-sass>chalk>supports-color": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gulp-dart-sass>chalk>ansi-styles": {
|
"gulp-dart-sass>chalk>ansi-styles": {
|
||||||
@ -3391,7 +3380,12 @@
|
|||||||
"process.versions.node.split": true
|
"process.versions.node.split": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>supports-color>has-flag": true
|
"gulp-dart-sass>chalk>supports-color>has-flag": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gulp-dart-sass>chalk>supports-color>has-flag": {
|
||||||
|
"globals": {
|
||||||
|
"process.argv": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gulp-dart-sass>strip-ansi": {
|
"gulp-dart-sass>strip-ansi": {
|
||||||
@ -3430,9 +3424,9 @@
|
|||||||
"process.platform": true
|
"process.platform": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"gulp-dart-sass>chalk>escape-string-regexp": true,
|
||||||
"gulp-livereload>chalk>ansi-styles": true,
|
"gulp-livereload>chalk>ansi-styles": true,
|
||||||
"gulp-livereload>chalk>supports-color": true,
|
"gulp-livereload>chalk>supports-color": true
|
||||||
"mocha>escape-string-regexp": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gulp-livereload>chalk>ansi-styles": {
|
"gulp-livereload>chalk>ansi-styles": {
|
||||||
@ -3452,7 +3446,12 @@
|
|||||||
"process.versions.node.split": true
|
"process.versions.node.split": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>supports-color>has-flag": true
|
"gulp-livereload>chalk>supports-color>has-flag": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gulp-livereload>chalk>supports-color>has-flag": {
|
||||||
|
"globals": {
|
||||||
|
"process.argv": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gulp-livereload>debug": {
|
"gulp-livereload>debug": {
|
||||||
@ -3469,7 +3468,7 @@
|
|||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"gulp-livereload>chalk>supports-color": true,
|
"gulp-livereload>chalk>supports-color": true,
|
||||||
"gulp-livereload>debug>ms": true
|
"mocha>ms": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gulp-livereload>event-stream": {
|
"gulp-livereload>event-stream": {
|
||||||
@ -3595,7 +3594,7 @@
|
|||||||
"process": true
|
"process": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"gulp-livereload>debug>ms": true,
|
"mocha>ms": true,
|
||||||
"mocha>supports-color": true
|
"mocha>supports-color": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3699,9 +3698,9 @@
|
|||||||
"process.platform": true
|
"process.platform": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"gulp-dart-sass>chalk>escape-string-regexp": true,
|
||||||
"gulp-rtlcss>rtlcss>chalk>ansi-styles": true,
|
"gulp-rtlcss>rtlcss>chalk>ansi-styles": true,
|
||||||
"gulp-rtlcss>rtlcss>chalk>supports-color": true,
|
"gulp-rtlcss>rtlcss>chalk>supports-color": true
|
||||||
"mocha>escape-string-regexp": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gulp-rtlcss>rtlcss>chalk>ansi-styles": {
|
"gulp-rtlcss>rtlcss>chalk>ansi-styles": {
|
||||||
@ -3721,7 +3720,12 @@
|
|||||||
"process.versions.node.split": true
|
"process.versions.node.split": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>supports-color>has-flag": true
|
"gulp-rtlcss>rtlcss>chalk>supports-color>has-flag": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gulp-rtlcss>rtlcss>chalk>supports-color>has-flag": {
|
||||||
|
"globals": {
|
||||||
|
"process.argv": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gulp-rtlcss>rtlcss>postcss": {
|
"gulp-rtlcss>rtlcss>postcss": {
|
||||||
@ -3947,7 +3951,7 @@
|
|||||||
"process": true
|
"process": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"gulp-livereload>debug>ms": true,
|
"mocha>ms": true,
|
||||||
"mocha>supports-color": true
|
"mocha>supports-color": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -5204,11 +5208,11 @@
|
|||||||
"chokidar>normalize-path": true,
|
"chokidar>normalize-path": true,
|
||||||
"eslint>is-glob": true,
|
"eslint>is-glob": true,
|
||||||
"gulp-watch>chokidar>async-each": true,
|
"gulp-watch>chokidar>async-each": true,
|
||||||
|
"gulp-watch>glob-parent": true,
|
||||||
"gulp-watch>path-is-absolute": true,
|
"gulp-watch>path-is-absolute": true,
|
||||||
"gulp>glob-watcher>anymatch": true,
|
"gulp>glob-watcher>anymatch": true,
|
||||||
"gulp>glob-watcher>chokidar>braces": true,
|
"gulp>glob-watcher>chokidar>braces": true,
|
||||||
"gulp>glob-watcher>chokidar>fsevents": true,
|
"gulp>glob-watcher>chokidar>fsevents": true,
|
||||||
"gulp>glob-watcher>chokidar>glob-parent": true,
|
|
||||||
"gulp>glob-watcher>chokidar>is-binary-path": true,
|
"gulp>glob-watcher>chokidar>is-binary-path": true,
|
||||||
"gulp>glob-watcher>chokidar>readdirp": true,
|
"gulp>glob-watcher>chokidar>readdirp": true,
|
||||||
"gulp>glob-watcher>chokidar>upath": true,
|
"gulp>glob-watcher>chokidar>upath": true,
|
||||||
@ -5274,21 +5278,6 @@
|
|||||||
"gulp-watch>chokidar>fsevents>node-pre-gyp": true
|
"gulp-watch>chokidar>fsevents>node-pre-gyp": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gulp>glob-watcher>chokidar>glob-parent": {
|
|
||||||
"builtin": {
|
|
||||||
"os.platform": true,
|
|
||||||
"path": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"gulp-watch>glob-parent>path-dirname": true,
|
|
||||||
"gulp>glob-watcher>chokidar>glob-parent>is-glob": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gulp>glob-watcher>chokidar>glob-parent>is-glob": {
|
|
||||||
"packages": {
|
|
||||||
"gulp>glob-watcher>chokidar>glob-parent>is-glob>is-extglob": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gulp>glob-watcher>chokidar>is-binary-path": {
|
"gulp>glob-watcher>chokidar>is-binary-path": {
|
||||||
"builtin": {
|
"builtin": {
|
||||||
"path.extname": true
|
"path.extname": true
|
||||||
@ -5663,8 +5652,8 @@
|
|||||||
"process.nextTick": true
|
"process.nextTick": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"gulp-watch>glob-parent": true,
|
||||||
"gulp>glob-watcher>is-negated-glob": true,
|
"gulp>glob-watcher>is-negated-glob": true,
|
||||||
"gulp>vinyl-fs>glob-stream>glob-parent": true,
|
|
||||||
"gulp>vinyl-fs>glob-stream>ordered-read-streams": true,
|
"gulp>vinyl-fs>glob-stream>ordered-read-streams": true,
|
||||||
"gulp>vinyl-fs>glob-stream>pumpify": true,
|
"gulp>vinyl-fs>glob-stream>pumpify": true,
|
||||||
"gulp>vinyl-fs>glob-stream>to-absolute-glob": true,
|
"gulp>vinyl-fs>glob-stream>to-absolute-glob": true,
|
||||||
@ -5675,21 +5664,6 @@
|
|||||||
"vinyl>remove-trailing-separator": true
|
"vinyl>remove-trailing-separator": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gulp>vinyl-fs>glob-stream>glob-parent": {
|
|
||||||
"builtin": {
|
|
||||||
"os.platform": true,
|
|
||||||
"path": true
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"gulp-watch>glob-parent>path-dirname": true,
|
|
||||||
"gulp>vinyl-fs>glob-stream>glob-parent>is-glob": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gulp>vinyl-fs>glob-stream>glob-parent>is-glob": {
|
|
||||||
"packages": {
|
|
||||||
"gulp>vinyl-fs>glob-stream>glob-parent>is-glob>is-extglob": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gulp>vinyl-fs>glob-stream>ordered-read-streams": {
|
"gulp>vinyl-fs>glob-stream>ordered-read-streams": {
|
||||||
"builtin": {
|
"builtin": {
|
||||||
"util.inherits": true
|
"util.inherits": true
|
||||||
@ -5791,7 +5765,7 @@
|
|||||||
"gulp>vinyl-fs>object.assign": {
|
"gulp>vinyl-fs>object.assign": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"globalthis>define-properties": true,
|
"globalthis>define-properties": true,
|
||||||
"mocha>object.assign>object-keys": true,
|
"globalthis>define-properties>object-keys": true,
|
||||||
"string.prototype.matchall>call-bind": true,
|
"string.prototype.matchall>call-bind": true,
|
||||||
"string.prototype.matchall>has-symbols": true
|
"string.prototype.matchall>has-symbols": true
|
||||||
}
|
}
|
||||||
@ -6092,9 +6066,9 @@
|
|||||||
"process.platform": true
|
"process.platform": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"gulp-dart-sass>chalk>escape-string-regexp": true,
|
||||||
"lavamoat>@babel/highlight>chalk>ansi-styles": true,
|
"lavamoat>@babel/highlight>chalk>ansi-styles": true,
|
||||||
"lavamoat>@babel/highlight>chalk>supports-color": true,
|
"lavamoat>@babel/highlight>chalk>supports-color": true
|
||||||
"mocha>escape-string-regexp": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lavamoat>@babel/highlight>chalk>ansi-styles": {
|
"lavamoat>@babel/highlight>chalk>ansi-styles": {
|
||||||
@ -6114,7 +6088,12 @@
|
|||||||
"process.versions.node.split": true
|
"process.versions.node.split": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>supports-color>has-flag": true
|
"lavamoat>@babel/highlight>chalk>supports-color>has-flag": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lavamoat>@babel/highlight>chalk>supports-color>has-flag": {
|
||||||
|
"globals": {
|
||||||
|
"process.argv": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lavamoat>@lavamoat/aa": {
|
"lavamoat>@lavamoat/aa": {
|
||||||
@ -6272,6 +6251,31 @@
|
|||||||
"process.platform": true
|
"process.platform": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mocha>log-symbols": {
|
||||||
|
"packages": {
|
||||||
|
"madge>ora>is-unicode-supported": true,
|
||||||
|
"mocha>log-symbols>chalk": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mocha>log-symbols>chalk": {
|
||||||
|
"packages": {
|
||||||
|
"chalk>ansi-styles": true,
|
||||||
|
"mocha>log-symbols>chalk>supports-color": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mocha>log-symbols>chalk>supports-color": {
|
||||||
|
"builtin": {
|
||||||
|
"os.release": true,
|
||||||
|
"tty.isatty": true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"process.env": true,
|
||||||
|
"process.platform": true
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"sinon>supports-color>has-flag": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"mocha>minimatch>brace-expansion": {
|
"mocha>minimatch>brace-expansion": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>minimatch>brace-expansion>concat-map": true,
|
"mocha>minimatch>brace-expansion>concat-map": true,
|
||||||
@ -6280,22 +6284,15 @@
|
|||||||
},
|
},
|
||||||
"mocha>supports-color": {
|
"mocha>supports-color": {
|
||||||
"builtin": {
|
"builtin": {
|
||||||
"os.release": true
|
"os.release": true,
|
||||||
|
"tty.isatty": true
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"process.env": true,
|
"process.env": true,
|
||||||
"process.platform": true,
|
"process.platform": true
|
||||||
"process.stderr": true,
|
|
||||||
"process.stdout": true,
|
|
||||||
"process.versions.node.split": true
|
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>supports-color>has-flag": true
|
"sinon>supports-color>has-flag": true
|
||||||
}
|
|
||||||
},
|
|
||||||
"mocha>supports-color>has-flag": {
|
|
||||||
"globals": {
|
|
||||||
"process.argv": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mocha>which": {
|
"mocha>which": {
|
||||||
@ -6565,9 +6562,9 @@
|
|||||||
"react-markdown>unified": {
|
"react-markdown>unified": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"jsdom>request>extend": true,
|
"jsdom>request>extend": true,
|
||||||
|
"mocha>yargs-unparser>is-plain-obj": true,
|
||||||
"react-markdown>unified>bail": true,
|
"react-markdown>unified>bail": true,
|
||||||
"react-markdown>unified>is-buffer": true,
|
"react-markdown>unified>is-buffer": true,
|
||||||
"react-markdown>unified>is-plain-obj": true,
|
|
||||||
"react-markdown>unified>trough": true,
|
"react-markdown>unified>trough": true,
|
||||||
"react-markdown>vfile": true
|
"react-markdown>vfile": true
|
||||||
}
|
}
|
||||||
@ -6767,6 +6764,11 @@
|
|||||||
"process.platform": true
|
"process.platform": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sinon>supports-color>has-flag": {
|
||||||
|
"globals": {
|
||||||
|
"process.argv": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"source-map": {
|
"source-map": {
|
||||||
"builtin": {
|
"builtin": {
|
||||||
"fs.readFile": true,
|
"fs.readFile": true,
|
||||||
@ -6801,7 +6803,7 @@
|
|||||||
},
|
},
|
||||||
"string.prototype.matchall>call-bind": {
|
"string.prototype.matchall>call-bind": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>object.assign>function-bind": true,
|
"browserify>has>function-bind": true,
|
||||||
"string.prototype.matchall>get-intrinsic": true
|
"string.prototype.matchall>get-intrinsic": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -6840,7 +6842,7 @@
|
|||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"browserify>has": true,
|
"browserify>has": true,
|
||||||
"mocha>object.assign>function-bind": true,
|
"browserify>has>function-bind": true,
|
||||||
"string.prototype.matchall>has-symbols": true
|
"string.prototype.matchall>has-symbols": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -6896,6 +6898,7 @@
|
|||||||
"globby>ignore": true,
|
"globby>ignore": true,
|
||||||
"globby>slash": true,
|
"globby>slash": true,
|
||||||
"lodash": true,
|
"lodash": true,
|
||||||
|
"mocha>log-symbols": true,
|
||||||
"nock>debug": true,
|
"nock>debug": true,
|
||||||
"nyc>resolve-from": true,
|
"nyc>resolve-from": true,
|
||||||
"stylelint>@stylelint/postcss-css-in-js": true,
|
"stylelint>@stylelint/postcss-css-in-js": true,
|
||||||
@ -6912,7 +6915,6 @@
|
|||||||
"stylelint>import-lazy": true,
|
"stylelint>import-lazy": true,
|
||||||
"stylelint>known-css-properties": true,
|
"stylelint>known-css-properties": true,
|
||||||
"stylelint>leven": true,
|
"stylelint>leven": true,
|
||||||
"stylelint>log-symbols": true,
|
|
||||||
"stylelint>mathml-tag-names": true,
|
"stylelint>mathml-tag-names": true,
|
||||||
"stylelint>micromatch": true,
|
"stylelint>micromatch": true,
|
||||||
"stylelint>normalize-selector": true,
|
"stylelint>normalize-selector": true,
|
||||||
@ -7094,12 +7096,7 @@
|
|||||||
"process.platform": true
|
"process.platform": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"stylelint>chalk>supports-color>has-flag": true
|
"sinon>supports-color>has-flag": true
|
||||||
}
|
|
||||||
},
|
|
||||||
"stylelint>chalk>supports-color>has-flag": {
|
|
||||||
"globals": {
|
|
||||||
"process.argv": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stylelint>cosmiconfig": {
|
"stylelint>cosmiconfig": {
|
||||||
@ -7212,8 +7209,8 @@
|
|||||||
"process.platform": true
|
"process.platform": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>which": true,
|
"stylelint>global-modules>global-prefix>ini": true,
|
||||||
"stylelint>global-modules>global-prefix>ini": true
|
"stylelint>global-modules>global-prefix>which": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stylelint>global-modules>global-prefix>ini": {
|
"stylelint>global-modules>global-prefix>ini": {
|
||||||
@ -7221,39 +7218,24 @@
|
|||||||
"process": true
|
"process": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stylelint>globjoin": {
|
"stylelint>global-modules>global-prefix>which": {
|
||||||
"builtin": {
|
"builtin": {
|
||||||
"path.join": true
|
"path.join": true
|
||||||
}
|
|
||||||
},
|
|
||||||
"stylelint>log-symbols": {
|
|
||||||
"packages": {
|
|
||||||
"madge>ora>is-unicode-supported": true,
|
|
||||||
"stylelint>log-symbols>chalk": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"stylelint>log-symbols>chalk": {
|
|
||||||
"packages": {
|
|
||||||
"chalk>ansi-styles": true,
|
|
||||||
"stylelint>log-symbols>chalk>supports-color": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"stylelint>log-symbols>chalk>supports-color": {
|
|
||||||
"builtin": {
|
|
||||||
"os.release": true,
|
|
||||||
"tty.isatty": true
|
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"process.env": true,
|
"process.cwd": true,
|
||||||
|
"process.env.OSTYPE": true,
|
||||||
|
"process.env.PATH": true,
|
||||||
|
"process.env.PATHEXT": true,
|
||||||
"process.platform": true
|
"process.platform": true
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"stylelint>log-symbols>chalk>supports-color>has-flag": true
|
"mocha>which>isexe": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stylelint>log-symbols>chalk>supports-color>has-flag": {
|
"stylelint>globjoin": {
|
||||||
"globals": {
|
"builtin": {
|
||||||
"process.argv": true
|
"path.join": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stylelint>micromatch": {
|
"stylelint>micromatch": {
|
||||||
@ -7557,9 +7539,9 @@
|
|||||||
},
|
},
|
||||||
"stylelint>table>slice-ansi": {
|
"stylelint>table>slice-ansi": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>yargs>string-width>is-fullwidth-code-point": true,
|
|
||||||
"stylelint>table>slice-ansi>ansi-styles": true,
|
"stylelint>table>slice-ansi>ansi-styles": true,
|
||||||
"stylelint>table>slice-ansi>astral-regex": true
|
"stylelint>table>slice-ansi>astral-regex": true,
|
||||||
|
"stylelint>table>slice-ansi>is-fullwidth-code-point": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stylelint>table>slice-ansi>ansi-styles": {
|
"stylelint>table>slice-ansi>ansi-styles": {
|
||||||
@ -7569,7 +7551,7 @@
|
|||||||
},
|
},
|
||||||
"stylelint>table>string-width": {
|
"stylelint>table>string-width": {
|
||||||
"packages": {
|
"packages": {
|
||||||
"mocha>yargs>string-width>is-fullwidth-code-point": true,
|
"stylelint>table>slice-ansi>is-fullwidth-code-point": true,
|
||||||
"stylelint>table>string-width>emoji-regex": true,
|
"stylelint>table>string-width>emoji-regex": true,
|
||||||
"stylelint>table>string-width>strip-ansi": true
|
"stylelint>table>string-width>strip-ansi": true
|
||||||
}
|
}
|
||||||
|
30
package.json
30
package.json
@ -226,11 +226,11 @@
|
|||||||
"@keystonehq/metamask-airgapped-keyring": "^0.6.1",
|
"@keystonehq/metamask-airgapped-keyring": "^0.6.1",
|
||||||
"@lavamoat/snow": "^1.5.0",
|
"@lavamoat/snow": "^1.5.0",
|
||||||
"@material-ui/core": "^4.11.0",
|
"@material-ui/core": "^4.11.0",
|
||||||
"@metamask/address-book-controller": "^1.0.0",
|
"@metamask/address-book-controller": "^2.0.0",
|
||||||
"@metamask/announcement-controller": "^2.0.1",
|
"@metamask/announcement-controller": "^3.0.0",
|
||||||
"@metamask/approval-controller": "^1.0.0",
|
"@metamask/approval-controller": "^2.1.0",
|
||||||
"@metamask/assets-controllers": "^4.0.1",
|
"@metamask/assets-controllers": "^5.0.0",
|
||||||
"@metamask/base-controller": "^1.0.0",
|
"@metamask/base-controller": "^2.0.0",
|
||||||
"@metamask/contract-metadata": "^2.3.1",
|
"@metamask/contract-metadata": "^2.3.1",
|
||||||
"@metamask/controller-utils": "^3.1.0",
|
"@metamask/controller-utils": "^3.1.0",
|
||||||
"@metamask/design-tokens": "^1.9.0",
|
"@metamask/design-tokens": "^1.9.0",
|
||||||
@ -242,19 +242,19 @@
|
|||||||
"@metamask/eth-ledger-bridge-keyring": "^0.13.0",
|
"@metamask/eth-ledger-bridge-keyring": "^0.13.0",
|
||||||
"@metamask/eth-token-tracker": "^4.0.0",
|
"@metamask/eth-token-tracker": "^4.0.0",
|
||||||
"@metamask/etherscan-link": "^2.2.0",
|
"@metamask/etherscan-link": "^2.2.0",
|
||||||
"@metamask/gas-fee-controller": "^1.0.0",
|
"@metamask/gas-fee-controller": "^4.0.0",
|
||||||
"@metamask/jazzicon": "^2.0.0",
|
"@metamask/jazzicon": "^2.0.0",
|
||||||
"@metamask/key-tree": "^7.0.0",
|
"@metamask/key-tree": "^7.0.0",
|
||||||
"@metamask/logo": "^3.1.1",
|
"@metamask/logo": "^3.1.1",
|
||||||
"@metamask/message-manager": "^2.1.0",
|
"@metamask/message-manager": "^3.0.0",
|
||||||
"@metamask/metamask-eth-abis": "^3.0.0",
|
"@metamask/metamask-eth-abis": "^3.0.0",
|
||||||
"@metamask/notification-controller": "^1.0.0",
|
"@metamask/notification-controller": "^2.0.0",
|
||||||
"@metamask/obs-store": "^8.0.0",
|
"@metamask/obs-store": "^8.1.0",
|
||||||
"@metamask/permission-controller": "^3.1.0",
|
"@metamask/permission-controller": "^3.1.0",
|
||||||
"@metamask/phishing-controller": "^2.0.0",
|
"@metamask/phishing-controller": "^3.0.0",
|
||||||
"@metamask/post-message-stream": "^6.0.0",
|
"@metamask/post-message-stream": "^6.0.0",
|
||||||
"@metamask/providers": "^10.2.1",
|
"@metamask/providers": "^10.2.1",
|
||||||
"@metamask/rate-limit-controller": "^1.0.0",
|
"@metamask/rate-limit-controller": "^2.0.0",
|
||||||
"@metamask/rpc-methods": "^0.32.2",
|
"@metamask/rpc-methods": "^0.32.2",
|
||||||
"@metamask/safe-event-emitter": "^2.0.0",
|
"@metamask/safe-event-emitter": "^2.0.0",
|
||||||
"@metamask/scure-bip39": "^2.0.3",
|
"@metamask/scure-bip39": "^2.0.3",
|
||||||
@ -263,7 +263,7 @@
|
|||||||
"@metamask/snaps-controllers": "^0.32.2",
|
"@metamask/snaps-controllers": "^0.32.2",
|
||||||
"@metamask/snaps-ui": "^0.32.2",
|
"@metamask/snaps-ui": "^0.32.2",
|
||||||
"@metamask/snaps-utils": "^0.32.2",
|
"@metamask/snaps-utils": "^0.32.2",
|
||||||
"@metamask/subject-metadata-controller": "^1.0.0",
|
"@metamask/subject-metadata-controller": "^2.0.0",
|
||||||
"@metamask/swappable-obj-proxy": "^2.1.0",
|
"@metamask/swappable-obj-proxy": "^2.1.0",
|
||||||
"@metamask/utils": "^5.0.0",
|
"@metamask/utils": "^5.0.0",
|
||||||
"@ngraveio/bc-ur": "^1.1.6",
|
"@ngraveio/bc-ur": "^1.1.6",
|
||||||
@ -368,6 +368,7 @@
|
|||||||
"@babel/preset-typescript": "^7.16.7",
|
"@babel/preset-typescript": "^7.16.7",
|
||||||
"@babel/register": "^7.5.5",
|
"@babel/register": "^7.5.5",
|
||||||
"@ethersproject/bignumber": "^5.7.0",
|
"@ethersproject/bignumber": "^5.7.0",
|
||||||
|
"@json-rpc-specification/meta-schema": "^1.0.6",
|
||||||
"@lavamoat/allow-scripts": "^2.0.3",
|
"@lavamoat/allow-scripts": "^2.0.3",
|
||||||
"@lavamoat/lavapack": "^5.0.0",
|
"@lavamoat/lavapack": "^5.0.0",
|
||||||
"@metamask/auto-changelog": "^2.1.0",
|
"@metamask/auto-changelog": "^2.1.0",
|
||||||
@ -419,6 +420,7 @@
|
|||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"@types/react-redux": "^7.1.25",
|
"@types/react-redux": "^7.1.25",
|
||||||
"@types/remote-redux-devtools": "^0.5.5",
|
"@types/remote-redux-devtools": "^0.5.5",
|
||||||
|
"@types/sinon": "^10.0.13",
|
||||||
"@types/w3c-web-hid": "^1.0.3",
|
"@types/w3c-web-hid": "^1.0.3",
|
||||||
"@types/watchify": "^3.11.1",
|
"@types/watchify": "^3.11.1",
|
||||||
"@types/yargs": "^17.0.8",
|
"@types/yargs": "^17.0.8",
|
||||||
@ -451,7 +453,7 @@
|
|||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"eslint-plugin-jest": "^26.6.0",
|
"eslint-plugin-jest": "^26.6.0",
|
||||||
"eslint-plugin-jsdoc": "^39.3.3",
|
"eslint-plugin-jsdoc": "^39.3.3",
|
||||||
"eslint-plugin-mocha": "^8.1.0",
|
"eslint-plugin-mocha": "^10.1.0",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-react": "^7.23.1",
|
"eslint-plugin-react": "^7.23.1",
|
||||||
@ -495,7 +497,7 @@
|
|||||||
"lockfile-lint": "^4.9.6",
|
"lockfile-lint": "^4.9.6",
|
||||||
"loose-envify": "^1.4.0",
|
"loose-envify": "^1.4.0",
|
||||||
"madge": "^5.0.1",
|
"madge": "^5.0.1",
|
||||||
"mocha": "^7.2.0",
|
"mocha": "^9.2.2",
|
||||||
"mockttp": "^2.6.0",
|
"mockttp": "^2.6.0",
|
||||||
"nock": "^13.2.9",
|
"nock": "^13.2.9",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
|
@ -53,6 +53,7 @@ export const MESSAGE_TYPE = {
|
|||||||
PERSONAL_SIGN: 'personal_sign',
|
PERSONAL_SIGN: 'personal_sign',
|
||||||
SEND_METADATA: 'metamask_sendDomainMetadata',
|
SEND_METADATA: 'metamask_sendDomainMetadata',
|
||||||
SWITCH_ETHEREUM_CHAIN: 'wallet_switchEthereumChain',
|
SWITCH_ETHEREUM_CHAIN: 'wallet_switchEthereumChain',
|
||||||
|
TRANSACTION: 'transaction',
|
||||||
WALLET_REQUEST_PERMISSIONS: 'wallet_requestPermissions',
|
WALLET_REQUEST_PERMISSIONS: 'wallet_requestPermissions',
|
||||||
WATCH_ASSET: 'wallet_watchAsset',
|
WATCH_ASSET: 'wallet_watchAsset',
|
||||||
WATCH_ASSET_LEGACY: 'metamask_watchAsset',
|
WATCH_ASSET_LEGACY: 'metamask_watchAsset',
|
||||||
|
@ -510,6 +510,8 @@ export enum MetaMetricsEventName {
|
|||||||
SignatureFailed = 'Signature Failed',
|
SignatureFailed = 'Signature Failed',
|
||||||
SignatureRejected = 'Signature Rejected',
|
SignatureRejected = 'Signature Rejected',
|
||||||
SignatureRequested = 'Signature Requested',
|
SignatureRequested = 'Signature Requested',
|
||||||
|
TermsOfUseShown = 'Terms of Use Shown',
|
||||||
|
TermsOfUseAccepted = 'Terms of Use Accepted',
|
||||||
TokenImportButtonClicked = 'Import Token Button Clicked',
|
TokenImportButtonClicked = 'Import Token Button Clicked',
|
||||||
TokenScreenOpened = 'Token Screen Opened',
|
TokenScreenOpened = 'Token Screen Opened',
|
||||||
SupportLinkClicked = 'Support Link Clicked',
|
SupportLinkClicked = 'Support Link Clicked',
|
||||||
|
@ -238,7 +238,7 @@ export const INFURA_PROVIDER_TYPES = [
|
|||||||
NETWORK_TYPES.MAINNET,
|
NETWORK_TYPES.MAINNET,
|
||||||
NETWORK_TYPES.GOERLI,
|
NETWORK_TYPES.GOERLI,
|
||||||
NETWORK_TYPES.SEPOLIA,
|
NETWORK_TYPES.SEPOLIA,
|
||||||
];
|
] as const;
|
||||||
|
|
||||||
export const TEST_CHAINS = [
|
export const TEST_CHAINS = [
|
||||||
CHAIN_IDS.GOERLI,
|
CHAIN_IDS.GOERLI,
|
||||||
|
1
shared/constants/terms.js
Normal file
1
shared/constants/terms.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const TERMS_OF_USE_LAST_UPDATED = '2023-03-25';
|
@ -2,6 +2,13 @@
|
|||||||
"DNS": {
|
"DNS": {
|
||||||
"resolution": ""
|
"resolution": ""
|
||||||
},
|
},
|
||||||
|
"activeTab": {
|
||||||
|
"id": 113,
|
||||||
|
"title": "E2E Test Dapp",
|
||||||
|
"origin": "https://metamask.github.io",
|
||||||
|
"protocol": "https:",
|
||||||
|
"url": "https://metamask.github.io/test-dapp/"
|
||||||
|
},
|
||||||
"appState": {
|
"appState": {
|
||||||
"networkDropdownOpen": false,
|
"networkDropdownOpen": false,
|
||||||
"gasIsLoading": false,
|
"gasIsLoading": false,
|
||||||
@ -16,7 +23,8 @@
|
|||||||
"name": null
|
"name": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"warning": null
|
"warning": null,
|
||||||
|
"alertOpen": false
|
||||||
},
|
},
|
||||||
"confirmTransaction": {
|
"confirmTransaction": {
|
||||||
"txData": {
|
"txData": {
|
||||||
@ -42,6 +50,10 @@
|
|||||||
"history": {
|
"history": {
|
||||||
"mostRecentOverviewPage": "/mostRecentOverviewPage"
|
"mostRecentOverviewPage": "/mostRecentOverviewPage"
|
||||||
},
|
},
|
||||||
|
"invalidCustomNetwork": {
|
||||||
|
"state": "CLOSED",
|
||||||
|
"networkName": ""
|
||||||
|
},
|
||||||
"metamask": {
|
"metamask": {
|
||||||
"ipfsGateway": "",
|
"ipfsGateway": "",
|
||||||
"dismissSeedBackUpReminder": false,
|
"dismissSeedBackUpReminder": false,
|
||||||
@ -88,6 +100,14 @@
|
|||||||
"ensResolutionsByAddress": {},
|
"ensResolutionsByAddress": {},
|
||||||
"isAccountMenuOpen": false,
|
"isAccountMenuOpen": false,
|
||||||
"isUnlocked": true,
|
"isUnlocked": true,
|
||||||
|
"completedOnboarding": true,
|
||||||
|
"usedNetworks": {
|
||||||
|
"0x1": true,
|
||||||
|
"0x5": true,
|
||||||
|
"0x539": true
|
||||||
|
},
|
||||||
|
"showTestnetMessageInDropdown": true,
|
||||||
|
"networkConfigurations": {},
|
||||||
"alertEnabledness": {
|
"alertEnabledness": {
|
||||||
"unconnectedAccount": true
|
"unconnectedAccount": true
|
||||||
},
|
},
|
||||||
@ -1358,5 +1378,8 @@
|
|||||||
"balance": "0x4563918244f40000"
|
"balance": "0x4563918244f40000"
|
||||||
},
|
},
|
||||||
"stage": "DRAFT"
|
"stage": "DRAFT"
|
||||||
|
},
|
||||||
|
"unconnectedAccount": {
|
||||||
|
"state": "CLOSED"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1535,7 +1535,18 @@
|
|||||||
"origin": "tmashuang.github.io"
|
"origin": "tmashuang.github.io"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"desktopEnabled": false
|
"desktopEnabled": false,
|
||||||
|
"pendingApprovals": {
|
||||||
|
"testApprovalId": {
|
||||||
|
"id": "testApprovalId",
|
||||||
|
"time": 1528133319641,
|
||||||
|
"origin": "metamask",
|
||||||
|
"type": "transaction",
|
||||||
|
"requestData": { "txId": "testTransactionId" },
|
||||||
|
"requestState": { "test": "value" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pendingApprovalCount": 1
|
||||||
},
|
},
|
||||||
"send": {
|
"send": {
|
||||||
"amountMode": "INPUT",
|
"amountMode": "INPUT",
|
||||||
|
@ -140,6 +140,7 @@ function defaultFixture() {
|
|||||||
browserEnvironment: {},
|
browserEnvironment: {},
|
||||||
nftsDropdownState: {},
|
nftsDropdownState: {},
|
||||||
connectedStatusPopoverHasBeenShown: true,
|
connectedStatusPopoverHasBeenShown: true,
|
||||||
|
termsOfUseLastAgreed: 86400000000000,
|
||||||
defaultHomeActiveTabName: null,
|
defaultHomeActiveTabName: null,
|
||||||
fullScreenGasPollTokens: [],
|
fullScreenGasPollTokens: [],
|
||||||
notificationGasPollTokens: [],
|
notificationGasPollTokens: [],
|
||||||
|
@ -222,6 +222,9 @@ const getWindowHandles = async (driver, handlesCount) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const importSRPOnboardingFlow = async (driver, seedPhrase, password) => {
|
const importSRPOnboardingFlow = async (driver, seedPhrase, password) => {
|
||||||
|
// agree to terms of use
|
||||||
|
await driver.clickElement('[data-testid="onboarding-terms-checkbox"]');
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
await driver.clickElement('[data-testid="onboarding-import-wallet"]');
|
await driver.clickElement('[data-testid="onboarding-import-wallet"]');
|
||||||
|
|
||||||
@ -262,6 +265,9 @@ const completeImportSRPOnboardingFlowWordByWord = async (
|
|||||||
seedPhrase,
|
seedPhrase,
|
||||||
password,
|
password,
|
||||||
) => {
|
) => {
|
||||||
|
// agree to terms of use
|
||||||
|
await driver.clickElement('[data-testid="onboarding-terms-checkbox"]');
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
await driver.clickElement('[data-testid="onboarding-import-wallet"]');
|
await driver.clickElement('[data-testid="onboarding-import-wallet"]');
|
||||||
|
|
||||||
@ -293,6 +299,9 @@ const completeImportSRPOnboardingFlowWordByWord = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const completeCreateNewWalletOnboardingFlow = async (driver, password) => {
|
const completeCreateNewWalletOnboardingFlow = async (driver, password) => {
|
||||||
|
// agree to terms of use
|
||||||
|
await driver.clickElement('[data-testid="onboarding-terms-checkbox"]');
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
||||||
|
|
||||||
@ -342,6 +351,9 @@ const completeCreateNewWalletOnboardingFlow = async (driver, password) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const importWrongSRPOnboardingFlow = async (driver, seedPhrase) => {
|
const importWrongSRPOnboardingFlow = async (driver, seedPhrase) => {
|
||||||
|
// agree to terms of use
|
||||||
|
await driver.clickElement('[data-testid="onboarding-terms-checkbox"]');
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
await driver.clickElement('[data-testid="onboarding-import-wallet"]');
|
await driver.clickElement('[data-testid="onboarding-import-wallet"]');
|
||||||
|
|
||||||
|
@ -84,6 +84,7 @@ describe('MetaMask', function () {
|
|||||||
|
|
||||||
describe('Going through the first time flow', function () {
|
describe('Going through the first time flow', function () {
|
||||||
it('clicks the "Create New Wallet" button on the welcome screen', async function () {
|
it('clicks the "Create New Wallet" button on the welcome screen', async function () {
|
||||||
|
await driver.clickElement('[data-testid="onboarding-terms-checkbox"]');
|
||||||
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -62,9 +62,7 @@ describe('ERC721 NFTs testdapp interaction', function () {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Verify transaction
|
// Verify transaction
|
||||||
const completedTx = await driver.findElement('.list-item__title');
|
await driver.findElement({ text: 'Send TDC' });
|
||||||
const completedTxText = await completedTx.getText();
|
|
||||||
assert.equal(completedTxText, 'Send Token');
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -392,7 +392,9 @@ describe('Custom network', function () {
|
|||||||
text: 'Delete',
|
text: 'Delete',
|
||||||
});
|
});
|
||||||
|
|
||||||
await driver.findElement('.modal-container__footer');
|
await driver.waitForSelector('.modal-container__footer', {
|
||||||
|
timeout: 15000,
|
||||||
|
});
|
||||||
// should be deleted from the modal shown again to complete deletion custom network
|
// should be deleted from the modal shown again to complete deletion custom network
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
tag: 'button',
|
tag: 'button',
|
||||||
|
@ -119,16 +119,16 @@ describe('Create token, approve token and approve token without gas', function (
|
|||||||
);
|
);
|
||||||
|
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
text: 'Verify contract details',
|
text: 'Verify third-party details',
|
||||||
css: '.token-allowance-container__verify-link',
|
css: '.token-allowance-container__verify-link',
|
||||||
});
|
});
|
||||||
|
|
||||||
const modalTitle = await driver.waitForSelector({
|
const modalTitle = await driver.waitForSelector({
|
||||||
text: 'Contract details',
|
text: 'Third-party details',
|
||||||
tag: 'h5',
|
tag: 'h5',
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(await modalTitle.getText(), 'Contract details');
|
assert.equal(await modalTitle.getText(), 'Third-party details');
|
||||||
|
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
text: 'Got it',
|
text: 'Got it',
|
||||||
@ -292,10 +292,13 @@ describe('Create token, approve token and approve token without gas', function (
|
|||||||
await gasLimitInput.fill('60001');
|
await gasLimitInput.fill('60001');
|
||||||
await driver.clickElement({ text: 'Save', tag: 'button' });
|
await driver.clickElement({ text: 'Save', tag: 'button' });
|
||||||
|
|
||||||
await driver.waitForSelector({
|
await driver.waitForSelector(
|
||||||
|
{
|
||||||
css: '.box--flex-direction-row > h6',
|
css: '.box--flex-direction-row > h6',
|
||||||
text: '0.0006 ETH',
|
text: '0.0006 ETH',
|
||||||
});
|
},
|
||||||
|
{ timeout: 15000 },
|
||||||
|
);
|
||||||
|
|
||||||
// editing spending cap
|
// editing spending cap
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
|
@ -29,6 +29,8 @@ describe('Incremental Security', function () {
|
|||||||
},
|
},
|
||||||
async ({ driver }) => {
|
async ({ driver }) => {
|
||||||
await driver.navigate();
|
await driver.navigate();
|
||||||
|
// agree to terms of use
|
||||||
|
await driver.clickElement('[data-testid="onboarding-terms-checkbox"]');
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
||||||
|
@ -15,6 +15,8 @@ describe('MetaMask Responsive UI', function () {
|
|||||||
},
|
},
|
||||||
async ({ driver }) => {
|
async ({ driver }) => {
|
||||||
await driver.navigate();
|
await driver.navigate();
|
||||||
|
// agree to terms of use
|
||||||
|
await driver.clickElement('[data-testid="onboarding-terms-checkbox"]');
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
||||||
|
@ -108,6 +108,9 @@ describe('MetaMask onboarding', function () {
|
|||||||
async ({ driver }) => {
|
async ({ driver }) => {
|
||||||
await driver.navigate();
|
await driver.navigate();
|
||||||
|
|
||||||
|
// accept terms of use
|
||||||
|
await driver.clickElement('[data-testid="onboarding-terms-checkbox"]');
|
||||||
|
|
||||||
// welcome
|
// welcome
|
||||||
await driver.clickElement('[data-testid="onboarding-import-wallet"]');
|
await driver.clickElement('[data-testid="onboarding-import-wallet"]');
|
||||||
|
|
||||||
@ -145,6 +148,7 @@ describe('MetaMask onboarding', function () {
|
|||||||
async ({ driver }) => {
|
async ({ driver }) => {
|
||||||
await driver.navigate();
|
await driver.navigate();
|
||||||
|
|
||||||
|
await driver.clickElement('[data-testid="onboarding-terms-checkbox"]');
|
||||||
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
||||||
|
|
||||||
// metrics
|
// metrics
|
||||||
@ -208,6 +212,7 @@ describe('MetaMask onboarding', function () {
|
|||||||
async ({ driver }) => {
|
async ({ driver }) => {
|
||||||
await driver.navigate();
|
await driver.navigate();
|
||||||
|
|
||||||
|
await driver.clickElement('[data-testid="onboarding-terms-checkbox"]');
|
||||||
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
await driver.clickElement('[data-testid="onboarding-create-wallet"]');
|
||||||
|
|
||||||
// metrics
|
// metrics
|
||||||
|
@ -354,9 +354,12 @@ describe('Send ETH from dapp using advanced gas controls', function () {
|
|||||||
'.transaction-list-item__primary-currency',
|
'.transaction-list-item__primary-currency',
|
||||||
);
|
);
|
||||||
await txValue.click();
|
await txValue.click();
|
||||||
const baseFeeValue = await driver.waitForSelector({
|
const baseFeeValue = await driver.waitForSelector(
|
||||||
|
{
|
||||||
text: '0.000000025',
|
text: '0.000000025',
|
||||||
});
|
},
|
||||||
|
{ timeout: 15000 },
|
||||||
|
);
|
||||||
assert.equal(await baseFeeValue.getText(), '0.000000025');
|
assert.equal(await baseFeeValue.getText(), '0.000000025');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -60,7 +60,7 @@ describe('Sign Typed Data V4 Signature Request', function () {
|
|||||||
assert.equal(await origin.getText(), 'http://127.0.0.1:8080');
|
assert.equal(await origin.getText(), 'http://127.0.0.1:8080');
|
||||||
|
|
||||||
verifyContractDetailsButton.click();
|
verifyContractDetailsButton.click();
|
||||||
await driver.findElement({ text: 'Contract details', tag: 'h5' });
|
await driver.findElement({ text: 'Third-party details', tag: 'h5' });
|
||||||
await driver.findElement('[data-testid="recipient"]');
|
await driver.findElement('[data-testid="recipient"]');
|
||||||
await driver.clickElement({ text: 'Got it', tag: 'button' });
|
await driver.clickElement({ text: 'Got it', tag: 'button' });
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ describe('Sign Typed Data V3 Signature Request', function () {
|
|||||||
assert.equal(await origin.getText(), 'http://127.0.0.1:8080');
|
assert.equal(await origin.getText(), 'http://127.0.0.1:8080');
|
||||||
|
|
||||||
verifyContractDetailsButton.click();
|
verifyContractDetailsButton.click();
|
||||||
await driver.findElement({ text: 'Contract details', tag: 'h5' });
|
await driver.findElement({ text: 'Third-party details', tag: 'h5' });
|
||||||
await driver.findElement('[data-testid="recipient"]');
|
await driver.findElement('[data-testid="recipient"]');
|
||||||
await driver.clickElement({ text: 'Got it', tag: 'button' });
|
await driver.clickElement({ text: 'Got it', tag: 'button' });
|
||||||
|
|
||||||
|
6
types/eth-keyring-controller.d.ts
vendored
6
types/eth-keyring-controller.d.ts
vendored
@ -5,5 +5,11 @@ declare module '@metamask/eth-keyring-controller' {
|
|||||||
signPersonalMessage: (...any) => any;
|
signPersonalMessage: (...any) => any;
|
||||||
|
|
||||||
signTypedMessage: (...any) => any;
|
signTypedMessage: (...any) => any;
|
||||||
|
|
||||||
|
getKeyringForAccount: (address: string) => Promise<{
|
||||||
|
type: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
getEncryptionPublicKey: (address: string) => Promise<string>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
50
types/eth-query.d.ts
vendored
Normal file
50
types/eth-query.d.ts
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
declare module 'eth-query' {
|
||||||
|
// What it says on the tin. We omit `null` because confusingly, this is used
|
||||||
|
// for a successful response to indicate a lack of an error.
|
||||||
|
type EverythingButNull =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| object
|
||||||
|
| symbol
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
type ProviderSendAsyncResponse<Result> = {
|
||||||
|
error?: { message: string };
|
||||||
|
result?: Result;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ProviderSendAsyncCallback<Result> = (
|
||||||
|
error: unknown,
|
||||||
|
response: ProviderSendAsyncResponse<Result>,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
type Provider = {
|
||||||
|
sendAsync<Params, Result>(
|
||||||
|
payload: SendAsyncPayload<Params>,
|
||||||
|
callback: ProviderSendAsyncCallback<Result>,
|
||||||
|
): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SendAsyncPayload<Params> = {
|
||||||
|
id: number;
|
||||||
|
jsonrpc: '2.0';
|
||||||
|
method: string;
|
||||||
|
params: Params;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SendAsyncCallback<Result> = (
|
||||||
|
...args:
|
||||||
|
| [error: EverythingButNull, result: undefined]
|
||||||
|
| [error: null, result: Result]
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
export default class EthQuery {
|
||||||
|
constructor(provider: Provider);
|
||||||
|
|
||||||
|
sendAsync<Params, Result>(
|
||||||
|
opts: Partial<SendAsyncPayload<Params>>,
|
||||||
|
callback: SendAsyncCallback<Result>,
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
}
|
@ -82,6 +82,7 @@
|
|||||||
@import 'transaction-status-label/index';
|
@import 'transaction-status-label/index';
|
||||||
@import 'wallet-overview/index';
|
@import 'wallet-overview/index';
|
||||||
@import 'whats-new-popup/index';
|
@import 'whats-new-popup/index';
|
||||||
|
@import 'terms-of-use-popup/index';
|
||||||
@import 'loading-network-screen/index';
|
@import 'loading-network-screen/index';
|
||||||
@import 'transaction-decoding/index';
|
@import 'transaction-decoding/index';
|
||||||
@import 'advanced-gas-fee-popover/index';
|
@import 'advanced-gas-fee-popover/index';
|
||||||
|
@ -89,6 +89,7 @@ export default class AppHeader extends PureComponent {
|
|||||||
className={classnames('account-menu__icon', {
|
className={classnames('account-menu__icon', {
|
||||||
'account-menu__icon--disabled': disabled,
|
'account-menu__icon--disabled': disabled,
|
||||||
})}
|
})}
|
||||||
|
disabled={Boolean(disabled)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
!isAccountMenuOpen &&
|
!isAccountMenuOpen &&
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
import { BETA_BUGS_URL } from '../../../helpers/constants/beta';
|
import { BETA_BUGS_URL } from '../../../helpers/constants/beta';
|
||||||
|
|
||||||
import { hideBetaHeader } from '../../../store/actions';
|
import { hideBetaHeader } from '../../../store/actions';
|
||||||
import { ButtonIcon } from '../../component-library';
|
import { ButtonIcon } from '../../component-library/button-icon/deprecated';
|
||||||
import {
|
import {
|
||||||
ICON_NAMES,
|
ICON_NAMES,
|
||||||
ICON_SIZES,
|
ICON_SIZES,
|
||||||
|
@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
|||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { Menu } from '../../../ui/menu';
|
import { Menu } from '../../../ui/menu';
|
||||||
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
|
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
|
||||||
import { ButtonIcon } from '../../../component-library';
|
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
|
||||||
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||||
|
|
||||||
const ConnectedAccountsListOptions = ({
|
const ConnectedAccountsListOptions = ({
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useContext, useEffect } from 'react';
|
import React, { useState, useContext, useEffect, useRef } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
@ -41,6 +41,7 @@ export default function CustomSpendingCap({
|
|||||||
}) {
|
}) {
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const inputRef = useRef(null);
|
||||||
|
|
||||||
const value = useSelector(getCustomTokenAmount);
|
const value = useSelector(getCustomTokenAmount);
|
||||||
|
|
||||||
@ -139,6 +140,15 @@ export default function CustomSpendingCap({
|
|||||||
passTheErrorText(error);
|
passTheErrorText(error);
|
||||||
}, [error, passTheErrorText]);
|
}, [error, passTheErrorText]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.focus({
|
||||||
|
preventScroll: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [inputRef.current]);
|
||||||
|
|
||||||
const chooseTooltipContentText = decConversionGreaterThan(
|
const chooseTooltipContentText = decConversionGreaterThan(
|
||||||
value,
|
value,
|
||||||
currentTokenBalance,
|
currentTokenBalance,
|
||||||
@ -182,8 +192,8 @@ export default function CustomSpendingCap({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
|
inputRef={inputRef}
|
||||||
dataTestId="custom-spending-cap-input"
|
dataTestId="custom-spending-cap-input"
|
||||||
autoFocus
|
|
||||||
wrappingLabelProps={{ as: 'div' }}
|
wrappingLabelProps={{ as: 'div' }}
|
||||||
id={
|
id={
|
||||||
decConversionGreaterThan(value, currentTokenBalance)
|
decConversionGreaterThan(value, currentTokenBalance)
|
||||||
|
@ -20,7 +20,8 @@ import {
|
|||||||
getSnapName,
|
getSnapName,
|
||||||
removeSnapIdPrefix,
|
removeSnapIdPrefix,
|
||||||
} from '../../../../helpers/utils/util';
|
} from '../../../../helpers/utils/util';
|
||||||
import { Text, ButtonIcon } from '../../../component-library';
|
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
|
||||||
|
import { Text } from '../../../component-library';
|
||||||
import {
|
import {
|
||||||
ICON_NAMES,
|
ICON_NAMES,
|
||||||
ICON_SIZES,
|
ICON_SIZES,
|
||||||
|
1
ui/components/app/flask/snap-permissions-list/index.js
Normal file
1
ui/components/app/flask/snap-permissions-list/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './snap-permissions-list';
|
@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { getWeightedPermissions } from '../../../../helpers/utils/permission';
|
||||||
|
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||||
|
import PermissionCell from '../../permission-cell';
|
||||||
|
import Box from '../../../ui/box';
|
||||||
|
|
||||||
|
export default function SnapPermissionsList({
|
||||||
|
permissions,
|
||||||
|
targetSubjectMetadata,
|
||||||
|
}) {
|
||||||
|
const t = useI18nContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box paddingTop={2} paddingBottom={2} className="snap-permissions-list">
|
||||||
|
{getWeightedPermissions(t, permissions, targetSubjectMetadata).map(
|
||||||
|
(permission, index) => {
|
||||||
|
return (
|
||||||
|
<PermissionCell
|
||||||
|
title={permission.label}
|
||||||
|
description={permission.description}
|
||||||
|
weight={permission.weight}
|
||||||
|
avatarIcon={permission.leftIcon}
|
||||||
|
dateApproved={permission?.permissionValue?.date}
|
||||||
|
key={`${permission.permissionName}-${index}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SnapPermissionsList.propTypes = {
|
||||||
|
permissions: PropTypes.object.isRequired,
|
||||||
|
targetSubjectMetadata: PropTypes.object.isRequired,
|
||||||
|
};
|
@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import SnapPermissionsList from '.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/App/flask/SnapPermissionsList',
|
||||||
|
|
||||||
|
component: SnapPermissionsList,
|
||||||
|
argTypes: {
|
||||||
|
permissions: {
|
||||||
|
control: 'object',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => <SnapPermissionsList {...args} />;
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
||||||
|
|
||||||
|
DefaultStory.args = {
|
||||||
|
permissions: {
|
||||||
|
eth_accounts: {},
|
||||||
|
snap_dialog: {},
|
||||||
|
snap_getBip32PublicKey: {
|
||||||
|
caveats: [
|
||||||
|
{
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
path: ['m', `44'`, `0'`],
|
||||||
|
curve: 'secp256k1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import { renderWithProvider } from '../../../../../test/jest';
|
||||||
|
import SnapPermissionsList from './snap-permissions-list';
|
||||||
|
|
||||||
|
describe('Snap Permission List', () => {
|
||||||
|
const mockPermissionData = {
|
||||||
|
snap_dialog: {
|
||||||
|
caveats: null,
|
||||||
|
date: 1680709920602,
|
||||||
|
id: '4dduR1BpsmS0ZJfeVtiAh',
|
||||||
|
invoker: 'local:http://localhost:8080',
|
||||||
|
parentCapability: 'snap_dialog',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mockTargetSubjectMetadata = {
|
||||||
|
extensionId: null,
|
||||||
|
iconUrl: null,
|
||||||
|
name: 'TypeScript Example Snap',
|
||||||
|
origin: 'local:http://localhost:8080',
|
||||||
|
subjectType: 'snap',
|
||||||
|
version: '0.2.2',
|
||||||
|
};
|
||||||
|
|
||||||
|
it('renders permissions list for snaps', () => {
|
||||||
|
renderWithProvider(
|
||||||
|
<SnapPermissionsList
|
||||||
|
permissions={mockPermissionData}
|
||||||
|
targetSubjectMetadata={mockTargetSubjectMetadata}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
screen.getByText('Display dialog windows in MetaMask.'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Approved on 2023-04-05')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { object } from '@storybook/addon-knobs';
|
|
||||||
import { panel, text, heading, divider, copyable } from '@metamask/snaps-ui';
|
import { panel, text, heading, divider, copyable } from '@metamask/snaps-ui';
|
||||||
import configureStore from '../../../../store/store';
|
import configureStore from '../../../../store/store';
|
||||||
import testData from '../../../../../.storybook/test-data';
|
import testData from '../../../../../.storybook/test-data';
|
||||||
@ -10,8 +9,13 @@ const store = configureStore(testData);
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Components/App/SnapUIRenderer',
|
title: 'Components/App/SnapUIRenderer',
|
||||||
|
component: SnapUIRenderer,
|
||||||
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
|
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
|
||||||
|
argTypes: {
|
||||||
|
data: {
|
||||||
|
control: 'object',
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const DATA = panel([
|
const DATA = panel([
|
||||||
@ -22,13 +26,18 @@ const DATA = panel([
|
|||||||
copyable('Text you can copy'),
|
copyable('Text you can copy'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const DefaultStory = () => (
|
export const DefaultStory = (args) => (
|
||||||
<SnapUIRenderer
|
<SnapUIRenderer snapId="local:http://localhost:8080/" data={args.data} />
|
||||||
snapId="local:http://localhost:8080/"
|
|
||||||
data={object('data', DATA)}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export const ErrorStory = () => (
|
DefaultStory.args = {
|
||||||
<SnapUIRenderer snapId="local:http://localhost:8080/" data="foo" />
|
data: DATA,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ErrorStory = (args) => (
|
||||||
|
<SnapUIRenderer snapId="local:http://localhost:8080/" data={args.data} />
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ErrorStory.args = {
|
||||||
|
data: 'foo',
|
||||||
|
};
|
||||||
|
@ -170,8 +170,9 @@ export default function HoldToRevealButton({ buttonText, onLongPressed }) {
|
|||||||
onMouseDown={onMouseDown}
|
onMouseDown={onMouseDown}
|
||||||
onMouseUp={onMouseUp}
|
onMouseUp={onMouseUp}
|
||||||
className="hold-to-reveal-button__button-hold"
|
className="hold-to-reveal-button__button-hold"
|
||||||
|
textProps={{ display: DISPLAY.FLEX, alignItems: AlignItems.center }}
|
||||||
>
|
>
|
||||||
<Box className="hold-to-reveal-button__icon-container">
|
<Box className="hold-to-reveal-button__icon-container" marginRight={2}>
|
||||||
{renderPreCompleteContent()}
|
{renderPreCompleteContent()}
|
||||||
{renderPostCompleteContent()}
|
{renderPostCompleteContent()}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -14,9 +14,8 @@ import { CONNECTED_ACCOUNTS_ROUTE } from '../../../helpers/constants/routes';
|
|||||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||||
import { getOriginOfCurrentTab } from '../../../selectors';
|
import { getOriginOfCurrentTab } from '../../../selectors';
|
||||||
import { MetaMetricsContext } from '../../../contexts/metametrics';
|
import { MetaMetricsContext } from '../../../contexts/metametrics';
|
||||||
import { ButtonIcon } from '../../component-library';
|
import { ButtonIcon } from '../../component-library/button-icon/deprecated';
|
||||||
import { ICON_NAMES } from '../../component-library/icon/deprecated';
|
import { ICON_NAMES } from '../../component-library/icon/deprecated';
|
||||||
import { GlobalMenu } from '../../multichain/global-menu';
|
|
||||||
import AccountOptionsMenu from './account-options-menu';
|
import AccountOptionsMenu from './account-options-menu';
|
||||||
|
|
||||||
export default function MenuBar() {
|
export default function MenuBar() {
|
||||||
@ -34,7 +33,7 @@ export default function MenuBar() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="menu-bar">
|
<div className="menu-bar">
|
||||||
{showStatus ? ( // TODO: Move the connection status menu icon to the correct position in header once we implement the new header
|
{showStatus ? (
|
||||||
<ConnectedStatusIndicator
|
<ConnectedStatusIndicator
|
||||||
onClick={() => history.push(CONNECTED_ACCOUNTS_ROUTE)}
|
onClick={() => history.push(CONNECTED_ACCOUNTS_ROUTE)}
|
||||||
/>
|
/>
|
||||||
@ -58,18 +57,12 @@ export default function MenuBar() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
{accountOptionsMenuOpen &&
|
{accountOptionsMenuOpen && (
|
||||||
(process.env.MULTICHAIN ? (
|
|
||||||
<GlobalMenu
|
|
||||||
anchorElement={ref.current}
|
|
||||||
closeMenu={() => setAccountOptionsMenuOpen(false)}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<AccountOptionsMenu
|
<AccountOptionsMenu
|
||||||
anchorElement={ref.current}
|
anchorElement={ref.current}
|
||||||
onClose={() => setAccountOptionsMenuOpen(false)}
|
onClose={() => setAccountOptionsMenuOpen(false)}
|
||||||
/>
|
/>
|
||||||
))}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard';
|
|||||||
import { getAddressBookEntry } from '../../../../selectors';
|
import { getAddressBookEntry } from '../../../../selectors';
|
||||||
import { TokenStandard } from '../../../../../shared/constants/transaction';
|
import { TokenStandard } from '../../../../../shared/constants/transaction';
|
||||||
import NftCollectionImage from '../../../ui/nft-collection-image/nft-collection-image';
|
import NftCollectionImage from '../../../ui/nft-collection-image/nft-collection-image';
|
||||||
import { ButtonIcon } from '../../../component-library';
|
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
|
||||||
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
|
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
|
||||||
|
|
||||||
export default function ContractDetailsModal({
|
export default function ContractDetailsModal({
|
||||||
|
@ -15,7 +15,7 @@ import Box from '../../../ui/box';
|
|||||||
import withModalProps from '../../../../helpers/higher-order-components/with-modal-props';
|
import withModalProps from '../../../../helpers/higher-order-components/with-modal-props';
|
||||||
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||||
import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url';
|
import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url';
|
||||||
import { ButtonIcon } from '../../../component-library';
|
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
|
||||||
import {
|
import {
|
||||||
ICON_NAMES,
|
ICON_NAMES,
|
||||||
ICON_SIZES,
|
ICON_SIZES,
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
calcTokenAmount,
|
calcTokenAmount,
|
||||||
toPrecisionWithoutTrailingZeros,
|
toPrecisionWithoutTrailingZeros,
|
||||||
} from '../../../../../shared/lib/transactions-controller-utils';
|
} from '../../../../../shared/lib/transactions-controller-utils';
|
||||||
import { ButtonIcon } from '../../../component-library';
|
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
|
||||||
import {
|
import {
|
||||||
ICON_SIZES,
|
ICON_SIZES,
|
||||||
ICON_NAMES,
|
ICON_NAMES,
|
||||||
|
@ -2,12 +2,8 @@ import PropTypes from 'prop-types';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import withModalProps from '../../../../helpers/higher-order-components/with-modal-props';
|
import withModalProps from '../../../../helpers/higher-order-components/with-modal-props';
|
||||||
import Box from '../../../ui/box';
|
import Box from '../../../ui/box';
|
||||||
import {
|
import { Text, Button, BUTTON_TYPES } from '../../../component-library';
|
||||||
Text,
|
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
|
||||||
Button,
|
|
||||||
BUTTON_TYPES,
|
|
||||||
ButtonIcon,
|
|
||||||
} from '../../../component-library';
|
|
||||||
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
|
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
|
||||||
import {
|
import {
|
||||||
AlignItems,
|
AlignItems,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Button from '../../../ui/button/button.component';
|
import Button from '../../../ui/button/button.component';
|
||||||
import { ButtonIcon } from '../../../component-library';
|
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
|
||||||
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
|
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
|
||||||
|
|
||||||
export default class NewAccountModal extends Component {
|
export default class NewAccountModal extends Component {
|
||||||
|
@ -53,7 +53,7 @@ import {
|
|||||||
TokenStandard,
|
TokenStandard,
|
||||||
} from '../../../../shared/constants/transaction';
|
} from '../../../../shared/constants/transaction';
|
||||||
import NftDefaultImage from '../nft-default-image';
|
import NftDefaultImage from '../nft-default-image';
|
||||||
import { ButtonIcon } from '../../component-library';
|
import { ButtonIcon } from '../../component-library/button-icon/deprecated';
|
||||||
import { ICON_NAMES } from '../../component-library/icon/deprecated';
|
import { ICON_NAMES } from '../../component-library/icon/deprecated';
|
||||||
import Tooltip from '../../ui/tooltip';
|
import Tooltip from '../../ui/tooltip';
|
||||||
import { decWEIToDecETH } from '../../../../shared/modules/conversion.utils';
|
import { decWEIToDecETH } from '../../../../shared/modules/conversion.utils';
|
||||||
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
import { Menu, MenuItem } from '../../ui/menu';
|
import { Menu, MenuItem } from '../../ui/menu';
|
||||||
import { ButtonIcon } from '../../component-library';
|
import { ButtonIcon } from '../../component-library/button-icon/deprecated';
|
||||||
import { ICON_NAMES } from '../../component-library/icon/deprecated';
|
import { ICON_NAMES } from '../../component-library/icon/deprecated';
|
||||||
import { Color } from '../../../helpers/constants/design-system';
|
import { Color } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
@ -53,6 +53,11 @@ const PermissionCell = ({
|
|||||||
iconBackgroundColor = Color.backgroundAlternative;
|
iconBackgroundColor = Color.backgroundAlternative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let permissionIcon = avatarIcon;
|
||||||
|
if (typeof avatarIcon !== 'string' && avatarIcon?.props?.iconName) {
|
||||||
|
permissionIcon = avatarIcon.props.iconName;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
className="permission-cell"
|
className="permission-cell"
|
||||||
@ -64,9 +69,9 @@ const PermissionCell = ({
|
|||||||
paddingBottom={2}
|
paddingBottom={2}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
{typeof avatarIcon === 'string' ? (
|
{typeof permissionIcon === 'string' ? (
|
||||||
<AvatarIcon
|
<AvatarIcon
|
||||||
iconName={avatarIcon}
|
iconName={permissionIcon}
|
||||||
size={ICON_SIZES.MD}
|
size={ICON_SIZES.MD}
|
||||||
iconProps={{
|
iconProps={{
|
||||||
size: ICON_SIZES.SM,
|
size: ICON_SIZES.SM,
|
||||||
@ -75,7 +80,7 @@ const PermissionCell = ({
|
|||||||
backgroundColor={iconBackgroundColor}
|
backgroundColor={iconBackgroundColor}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
avatarIcon
|
permissionIcon
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box width="full" marginLeft={4} marginRight={4}>
|
<Box width="full" marginLeft={4} marginRight={4}>
|
||||||
|
@ -28,11 +28,14 @@ export default class PermissionPageContainerContent extends PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderRequestedPermissions() {
|
renderRequestedPermissions() {
|
||||||
const { selectedPermissions } = this.props;
|
const { selectedPermissions, subjectMetadata } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="permission-approval-container__content__requested">
|
<div className="permission-approval-container__content__requested">
|
||||||
<PermissionsConnectPermissionList permissions={selectedPermissions} />
|
<PermissionsConnectPermissionList
|
||||||
|
permissions={selectedPermissions}
|
||||||
|
targetSubjectMetadata={subjectMetadata}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,29 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { getWeightedPermissions } from '../../../helpers/utils/permission';
|
import {
|
||||||
|
getRightIcon,
|
||||||
|
getWeightedPermissions,
|
||||||
|
} from '../../../helpers/utils/permission';
|
||||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||||
import PermissionCell from '../permission-cell';
|
|
||||||
import Box from '../../ui/box';
|
/**
|
||||||
|
* Get one or more permission descriptions for a permission name.
|
||||||
|
*
|
||||||
|
* @param permission - The permission to render.
|
||||||
|
* @param index - The index of the permission.
|
||||||
|
* @returns {JSX.Element} A permission description node.
|
||||||
|
*/
|
||||||
|
function getDescriptionNode(permission, index) {
|
||||||
|
const { label, leftIcon, permissionName } = permission;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="permission" key={`${permissionName}-${index}`}>
|
||||||
|
{typeof leftIcon === 'string' ? <i className={leftIcon} /> : leftIcon}
|
||||||
|
{label}
|
||||||
|
{getRightIcon(permission)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function PermissionsConnectPermissionList({
|
export default function PermissionsConnectPermissionList({
|
||||||
permissions,
|
permissions,
|
||||||
@ -12,22 +32,11 @@ export default function PermissionsConnectPermissionList({
|
|||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box paddingTop={2} paddingBottom={2}>
|
<div className="permissions-connect-permission-list">
|
||||||
{getWeightedPermissions(t, permissions, targetSubjectMetadata).map(
|
{getWeightedPermissions(t, permissions, targetSubjectMetadata).map(
|
||||||
(permission, index) => {
|
getDescriptionNode,
|
||||||
return (
|
|
||||||
<PermissionCell
|
|
||||||
title={permission.label}
|
|
||||||
description={permission.description}
|
|
||||||
weight={permission.weight}
|
|
||||||
avatarIcon={permission.leftIcon}
|
|
||||||
dateApproved={permission?.permissionValue?.date}
|
|
||||||
key={`${permission.permissionName}-${index}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,18 +20,5 @@ DefaultStory.storyName = 'Default';
|
|||||||
DefaultStory.args = {
|
DefaultStory.args = {
|
||||||
permissions: {
|
permissions: {
|
||||||
eth_accounts: {},
|
eth_accounts: {},
|
||||||
snap_dialog: {},
|
|
||||||
snap_getBip32PublicKey: {
|
|
||||||
caveats: [
|
|
||||||
{
|
|
||||||
value: [
|
|
||||||
{
|
|
||||||
path: ['m', `44'`, `0'`],
|
|
||||||
curve: 'secp256k1',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,7 @@ import Popover from '../../ui/popover';
|
|||||||
import Checkbox from '../../ui/check-box';
|
import Checkbox from '../../ui/check-box';
|
||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
import { PageContainerFooter } from '../../ui/page-container';
|
import { PageContainerFooter } from '../../ui/page-container';
|
||||||
|
import { isAddressLedger } from '../../../ducks/metamask/metamask';
|
||||||
import {
|
import {
|
||||||
accountsWithSendEtherInfoSelector,
|
accountsWithSendEtherInfoSelector,
|
||||||
getSubjectMetadata,
|
getSubjectMetadata,
|
||||||
@ -20,6 +21,7 @@ import {
|
|||||||
|
|
||||||
import SecurityProviderBannerMessage from '../security-provider-banner-message/security-provider-banner-message';
|
import SecurityProviderBannerMessage from '../security-provider-banner-message/security-provider-banner-message';
|
||||||
import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants';
|
import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants';
|
||||||
|
import LedgerInstructionField from '../ledger-instruction-field';
|
||||||
import Header from './signature-request-siwe-header';
|
import Header from './signature-request-siwe-header';
|
||||||
import Message from './signature-request-siwe-message';
|
import Message from './signature-request-siwe-message';
|
||||||
|
|
||||||
@ -39,6 +41,8 @@ export default function SignatureRequestSIWE({
|
|||||||
},
|
},
|
||||||
} = txData;
|
} = txData;
|
||||||
|
|
||||||
|
const isLedgerWallet = useSelector((state) => isAddressLedger(state, from));
|
||||||
|
|
||||||
const fromAccount = getAccountByAddress(allAccounts, from);
|
const fromAccount = getAccountByAddress(allAccounts, from);
|
||||||
const targetSubjectMetadata = subjectMetadata[origin];
|
const targetSubjectMetadata = subjectMetadata[origin];
|
||||||
|
|
||||||
@ -115,6 +119,13 @@ export default function SignatureRequestSIWE({
|
|||||||
])}
|
])}
|
||||||
</BannerAlert>
|
</BannerAlert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isLedgerWallet && (
|
||||||
|
<div className="confirm-approve-content__ledger-instruction-wrapper">
|
||||||
|
<LedgerInstructionField showDataInstruction />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{!isSIWEDomainValid && (
|
{!isSIWEDomainValid && (
|
||||||
<BannerAlert
|
<BannerAlert
|
||||||
severity={SEVERITIES.DANGER}
|
severity={SEVERITIES.DANGER}
|
||||||
|
@ -52,6 +52,15 @@ const mockProps = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jest.mock('../ledger-instruction-field', () => {
|
||||||
|
return {
|
||||||
|
__esModule: true,
|
||||||
|
default: () => {
|
||||||
|
return <div className="mock-ledger-instruction-field" />;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const render = (txData = mockProps.txData) => {
|
const render = (txData = mockProps.txData) => {
|
||||||
const store = configureStore(mockStoreInitialState);
|
const store = configureStore(mockStoreInitialState);
|
||||||
|
|
||||||
@ -110,4 +119,21 @@ describe('SignatureRequestSIWE (Sign in with Ethereum)', () => {
|
|||||||
expect(bannerAlert).toBeTruthy();
|
expect(bannerAlert).toBeTruthy();
|
||||||
expect(bannerAlert).toHaveTextContent('Deceptive site request.');
|
expect(bannerAlert).toHaveTextContent('Deceptive site request.');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not show Ledger instructions if the address is not a Ledger address', () => {
|
||||||
|
const { container } = render();
|
||||||
|
expect(
|
||||||
|
container.querySelector('.mock-ledger-instruction-field'),
|
||||||
|
).not.toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show Ledger instructions if the address is a Ledger address', () => {
|
||||||
|
const mockTxData = cloneDeep(mockProps.txData);
|
||||||
|
mockTxData.msgParams.from = '0xc42edfcc21ed14dda456aa0756c153f7985d8813';
|
||||||
|
const { container } = render(mockTxData);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.querySelector('.mock-ledger-instruction-field'),
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -224,7 +224,7 @@ exports[`Signature Request Component render should match snapshot when we are us
|
|||||||
<h6
|
<h6
|
||||||
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography typography--h7 typography--weight-normal typography--style-normal typography--color-primary-default"
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography typography--h7 typography--weight-normal typography--style-normal typography--color-primary-default"
|
||||||
>
|
>
|
||||||
Verify contract details
|
Verify third-party details
|
||||||
</h6>
|
</h6>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -999,7 +999,7 @@ exports[`Signature Request Component render should match snapshot when we want t
|
|||||||
<h6
|
<h6
|
||||||
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography typography--h7 typography--weight-normal typography--style-normal typography--color-primary-default"
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography typography--h7 typography--weight-normal typography--style-normal typography--color-primary-default"
|
||||||
>
|
>
|
||||||
Verify contract details
|
Verify third-party details
|
||||||
</h6>
|
</h6>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
1
ui/components/app/terms-of-use-popup/index.js
Normal file
1
ui/components/app/terms-of-use-popup/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './terms-of-use-popup';
|
30
ui/components/app/terms-of-use-popup/index.scss
Normal file
30
ui/components/app/terms-of-use-popup/index.scss
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
.popover-wrap.terms-of-use__popover {
|
||||||
|
.terms-of-use {
|
||||||
|
&__terms-list {
|
||||||
|
list-style: decimal none outside;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer-text {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-header {
|
||||||
|
&__title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-footer {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include screen-sm-min {
|
||||||
|
max-height: 750px;
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include screen-sm-max {
|
||||||
|
max-height: 568px;
|
||||||
|
}
|
||||||
|
}
|
1181
ui/components/app/terms-of-use-popup/terms-of-use-popup.js
Normal file
1181
ui/components/app/terms-of-use-popup/terms-of-use-popup.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import TermsOfUsePopup from '.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/App/TermsOfUsePopup',
|
||||||
|
component: TermsOfUsePopup,
|
||||||
|
argTypes: {
|
||||||
|
onAccept: {
|
||||||
|
action: 'onAccept',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => <TermsOfUsePopup {...args} />;
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { fireEvent, screen } from '@testing-library/react';
|
||||||
|
import { renderWithProvider } from '../../../../test/jest';
|
||||||
|
import configureStore from '../../../store/store';
|
||||||
|
import mockState from '../../../../test/data/mock-state.json';
|
||||||
|
import TermsOfUsePopup from './terms-of-use-popup';
|
||||||
|
|
||||||
|
const render = () => {
|
||||||
|
const store = configureStore({
|
||||||
|
metamask: {
|
||||||
|
...mockState.metamask,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const onAccept = jest.fn();
|
||||||
|
return renderWithProvider(<TermsOfUsePopup onAccept={onAccept} />, store);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('TermsOfUsePopup', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const mockIntersectionObserver = jest.fn();
|
||||||
|
mockIntersectionObserver.mockReturnValue({
|
||||||
|
observe: () => null,
|
||||||
|
unobserve: () => null,
|
||||||
|
disconnect: () => null,
|
||||||
|
});
|
||||||
|
window.IntersectionObserver = mockIntersectionObserver;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders TermsOfUse component and shows Terms of Use text', () => {
|
||||||
|
render();
|
||||||
|
expect(
|
||||||
|
screen.getByText('Our Terms of Use have updated'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls down when handleScrollDownClick is called', () => {
|
||||||
|
render();
|
||||||
|
const mockScrollIntoView = jest.fn();
|
||||||
|
window.HTMLElement.prototype.scrollIntoView = mockScrollIntoView;
|
||||||
|
const button = document.querySelector(
|
||||||
|
"[data-testid='popover-scroll-button']",
|
||||||
|
);
|
||||||
|
fireEvent.click(button);
|
||||||
|
expect(mockScrollIntoView).toHaveBeenCalledWith({
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,12 +1,22 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils';
|
||||||
|
import { getSelectedIdentity } from '../../../selectors';
|
||||||
|
import { AddressCopyButton } from '../../multichain';
|
||||||
|
|
||||||
const WalletOverview = ({ balance, buttons, className, icon, loading }) => {
|
const WalletOverview = ({ balance, buttons, className, icon, loading }) => {
|
||||||
|
const selectedIdentity = useSelector(getSelectedIdentity);
|
||||||
|
const checksummedAddress = toChecksumHexAddress(selectedIdentity?.address);
|
||||||
return (
|
return (
|
||||||
<div className={classnames('wallet-overview', className)}>
|
<div className={classnames('wallet-overview', className)}>
|
||||||
<div className="wallet-overview__balance">
|
<div className="wallet-overview__balance">
|
||||||
{loading ? null : icon}
|
{process.env.MULTICHAIN ? (
|
||||||
|
<AddressCopyButton address={checksummedAddress} shorten />
|
||||||
|
) : (
|
||||||
|
<>{loading ? null : icon}</>
|
||||||
|
)}
|
||||||
{balance}
|
{balance}
|
||||||
</div>
|
</div>
|
||||||
<div className="wallet-overview__buttons">{buttons}</div>
|
<div className="wallet-overview__buttons">{buttons}</div>
|
||||||
|
@ -66,28 +66,6 @@
|
|||||||
height: 1px;
|
height: 1px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__scroll-button {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 12px;
|
|
||||||
right: 12px;
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
border-radius: 14px;
|
|
||||||
border: 1px solid var(--color-border-default);
|
|
||||||
background: var(--color-background-alternative);
|
|
||||||
color: var(--color-icon-default);
|
|
||||||
z-index: 201;
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.8;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover-wrap.whats-new-popup__popover {
|
.popover-wrap.whats-new-popup__popover {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { ButtonIcon, ButtonLink, Text } from '..';
|
import { ButtonIcon } from '../button-icon/deprecated';
|
||||||
import { IconName } from '../icon';
|
import { ButtonLink, Text } from '..';
|
||||||
|
import { ICON_NAMES } from '../icon/deprecated';
|
||||||
|
|
||||||
import Box from '../../ui/box';
|
import Box from '../../ui/box';
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ export const BannerBase = ({
|
|||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
className="mm-banner-base__close-button"
|
className="mm-banner-base__close-button"
|
||||||
marginLeft="auto"
|
marginLeft="auto"
|
||||||
iconName={IconName.Close}
|
iconName={ICON_NAMES.CLOSE}
|
||||||
size={Size.SM}
|
size={Size.SM}
|
||||||
ariaLabel="Close" // TODO: i18n
|
ariaLabel="Close" // TODO: i18n
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
@ -34,26 +34,23 @@ import { IconName } from '../icon';
|
|||||||
|
|
||||||
### Size
|
### Size
|
||||||
|
|
||||||
Use the `size` prop and the `Size` object from `./ui/helpers/constants/design-system.js`
|
Use the `size` prop and the `ButtonIconSize` enum from `./ui/components/component-library/icon` to change the size of `ButtonIcon`. Defaults to `ButtonIconSize.Sm`
|
||||||
to change the size of `ButtonIcon`. Defaults to `Size.SM`
|
|
||||||
|
|
||||||
Optional: `BUTTON_ICON_SIZES` from `./button-icon` object can be used instead of `Size`.
|
|
||||||
|
|
||||||
Possible sizes include:
|
Possible sizes include:
|
||||||
|
|
||||||
- `Size.SM` 24px
|
- `ButtonIconSize.Sm` 24px
|
||||||
- `Size.LG` 32px
|
- `ButtonIconSize.Lg` 32px
|
||||||
|
|
||||||
<Canvas>
|
<Canvas>
|
||||||
<Story id="components-componentlibrary-buttonicon--size-story" />
|
<Story id="components-componentlibrary-buttonicon--size-story" />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { Size } from '../../../helpers/constants/design-system';
|
import { ButtonIconSize } from '../../../helpers/constants/design-system';
|
||||||
import { ButtonIcon } from '../ui/component-library';
|
import { ButtonIcon } from '../ui/component-library';
|
||||||
|
|
||||||
<ButtonIcon size={Size.SM} iconName={IconName.Close} ariaLabel="Close"/>
|
<ButtonIcon size={ButtonIconSize.Sm} iconName={IconName.Close} ariaLabel="Close"/>
|
||||||
<ButtonIcon size={Size.LG} iconName={IconName.Close} ariaLabel="Close"/>
|
<ButtonIcon size={ButtonIconSize.Lg} iconName={IconName.Close} ariaLabel="Close"/>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Aria Label
|
### Aria Label
|
||||||
|
@ -1,35 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||||
AlignItems,
|
import { Color } from '../../../helpers/constants/design-system';
|
||||||
Color,
|
|
||||||
DISPLAY,
|
|
||||||
FLEX_DIRECTION,
|
|
||||||
Size,
|
|
||||||
} from '../../../helpers/constants/design-system';
|
|
||||||
import Box from '../../ui/box/box';
|
|
||||||
import { IconName } from '..';
|
import { IconName } from '..';
|
||||||
import { BUTTON_ICON_SIZES } from './button-icon.constants';
|
import { ButtonIconSize } from './button-icon.types';
|
||||||
import { ButtonIcon } from './button-icon';
|
import { ButtonIcon } from './button-icon';
|
||||||
import README from './README.mdx';
|
import README from './README.mdx';
|
||||||
|
|
||||||
const marginSizeControlOptions = [
|
|
||||||
undefined,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
5,
|
|
||||||
6,
|
|
||||||
7,
|
|
||||||
8,
|
|
||||||
9,
|
|
||||||
10,
|
|
||||||
11,
|
|
||||||
12,
|
|
||||||
'auto',
|
|
||||||
];
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Components/ComponentLibrary/ButtonIcon',
|
title: 'Components/ComponentLibrary/ButtonIcon',
|
||||||
|
|
||||||
@ -40,58 +16,18 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
argTypes: {
|
argTypes: {
|
||||||
ariaLabel: {
|
|
||||||
control: 'text',
|
|
||||||
},
|
|
||||||
as: {
|
as: {
|
||||||
control: 'select',
|
control: 'select',
|
||||||
options: ['button', 'a'],
|
options: ['button', 'a'],
|
||||||
},
|
},
|
||||||
className: {
|
|
||||||
control: 'text',
|
|
||||||
},
|
},
|
||||||
color: {
|
} as ComponentMeta<typeof ButtonIcon>;
|
||||||
control: 'select',
|
|
||||||
options: Object.values(Color),
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
control: 'boolean',
|
|
||||||
},
|
|
||||||
href: {
|
|
||||||
control: 'text',
|
|
||||||
},
|
|
||||||
iconName: {
|
|
||||||
control: 'select',
|
|
||||||
options: Object.values(IconName),
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
control: 'select',
|
|
||||||
options: Object.values(BUTTON_ICON_SIZES),
|
|
||||||
},
|
|
||||||
marginTop: {
|
|
||||||
options: marginSizeControlOptions,
|
|
||||||
control: 'select',
|
|
||||||
table: { category: 'box props' },
|
|
||||||
},
|
|
||||||
marginRight: {
|
|
||||||
options: marginSizeControlOptions,
|
|
||||||
control: 'select',
|
|
||||||
table: { category: 'box props' },
|
|
||||||
},
|
|
||||||
marginBottom: {
|
|
||||||
options: marginSizeControlOptions,
|
|
||||||
control: 'select',
|
|
||||||
table: { category: 'box props' },
|
|
||||||
},
|
|
||||||
marginLeft: {
|
|
||||||
options: marginSizeControlOptions,
|
|
||||||
control: 'select',
|
|
||||||
table: { category: 'box props' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DefaultStory = (args) => <ButtonIcon {...args} />;
|
const Template: ComponentStory<typeof ButtonIcon> = (args) => (
|
||||||
|
<ButtonIcon {...args} />
|
||||||
|
);
|
||||||
|
|
||||||
|
export const DefaultStory = Template.bind({});
|
||||||
|
|
||||||
DefaultStory.args = {
|
DefaultStory.args = {
|
||||||
iconName: IconName.Close,
|
iconName: IconName.Close,
|
||||||
@ -100,7 +36,9 @@ DefaultStory.args = {
|
|||||||
|
|
||||||
DefaultStory.storyName = 'Default';
|
DefaultStory.storyName = 'Default';
|
||||||
|
|
||||||
export const IconNameStory = (args) => <ButtonIcon {...args} />;
|
export const IconNameStory: ComponentStory<typeof ButtonIcon> = (args) => (
|
||||||
|
<ButtonIcon {...args} />
|
||||||
|
);
|
||||||
|
|
||||||
IconNameStory.args = {
|
IconNameStory.args = {
|
||||||
iconName: IconName.Close,
|
iconName: IconName.Close,
|
||||||
@ -109,32 +47,27 @@ IconNameStory.args = {
|
|||||||
|
|
||||||
IconNameStory.storyName = 'IconName';
|
IconNameStory.storyName = 'IconName';
|
||||||
|
|
||||||
export const SizeStory = (args) => (
|
export const SizeStory: ComponentStory<typeof ButtonIcon> = (args) => (
|
||||||
<Box
|
<>
|
||||||
display={DISPLAY.FLEX}
|
|
||||||
alignItems={AlignItems.baseline}
|
|
||||||
gap={1}
|
|
||||||
marginBottom={2}
|
|
||||||
>
|
|
||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
{...args}
|
{...args}
|
||||||
size={Size.SM}
|
size={ButtonIconSize.Sm}
|
||||||
iconName={IconName.Close}
|
iconName={IconName.Close}
|
||||||
ariaLabel="Close"
|
ariaLabel="Close"
|
||||||
/>
|
/>
|
||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
{...args}
|
{...args}
|
||||||
size={Size.LG}
|
size={ButtonIconSize.Lg}
|
||||||
color={Color.primaryDefault}
|
color={Color.primaryDefault}
|
||||||
iconName={IconName.Close}
|
iconName={IconName.Close}
|
||||||
ariaLabel="Close"
|
ariaLabel="Close"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
SizeStory.storyName = 'Size';
|
SizeStory.storyName = 'Size';
|
||||||
|
|
||||||
export const AriaLabel = (args) => (
|
export const AriaLabel: ComponentStory<typeof ButtonIcon> = (args) => (
|
||||||
<>
|
<>
|
||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
as="button"
|
as="button"
|
||||||
@ -154,8 +87,8 @@ export const AriaLabel = (args) => (
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const As = (args) => (
|
export const As: ComponentStory<typeof ButtonIcon> = (args) => (
|
||||||
<Box display={DISPLAY.FLEX} flexDirection={FLEX_DIRECTION.ROW} gap={2}>
|
<>
|
||||||
<ButtonIcon {...args} iconName={IconName.Close} ariaLabel="close" />
|
<ButtonIcon {...args} iconName={IconName.Close} ariaLabel="close" />
|
||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
as="a"
|
as="a"
|
||||||
@ -165,10 +98,10 @@ export const As = (args) => (
|
|||||||
iconName={IconName.Export}
|
iconName={IconName.Export}
|
||||||
ariaLabel="demo"
|
ariaLabel="demo"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Href = (args) => (
|
export const Href: ComponentStory<typeof ButtonIcon> = (args) => (
|
||||||
<ButtonIcon iconName={IconName.Export} {...args} target="_blank" />
|
<ButtonIcon iconName={IconName.Export} {...args} target="_blank" />
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -178,7 +111,7 @@ Href.args = {
|
|||||||
color: Color.primaryDefault,
|
color: Color.primaryDefault,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ColorStory = (args) => (
|
export const ColorStory: ComponentStory<typeof ButtonIcon> = (args) => (
|
||||||
<ButtonIcon {...args} iconName={IconName.Close} ariaLabel="close" />
|
<ButtonIcon {...args} iconName={IconName.Close} ariaLabel="close" />
|
||||||
);
|
);
|
||||||
ColorStory.storyName = 'Color';
|
ColorStory.storyName = 'Color';
|
||||||
@ -187,7 +120,7 @@ ColorStory.args = {
|
|||||||
color: Color.primaryDefault,
|
color: Color.primaryDefault,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Disabled = (args) => (
|
export const Disabled: ComponentStory<typeof ButtonIcon> = (args) => (
|
||||||
<ButtonIcon {...args} iconName={IconName.Close} ariaLabel="close" />
|
<ButtonIcon {...args} iconName={IconName.Close} ariaLabel="close" />
|
||||||
);
|
);
|
||||||
|
|
147
ui/components/component-library/button-icon/button-icon.test.tsx
Normal file
147
ui/components/component-library/button-icon/button-icon.test.tsx
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/* eslint-disable jest/require-top-level-describe */
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { IconColor } from '../../../helpers/constants/design-system';
|
||||||
|
import { IconName } from '..';
|
||||||
|
import { ButtonIconSize } from './button-icon.types';
|
||||||
|
import { ButtonIcon } from './button-icon';
|
||||||
|
|
||||||
|
describe('ButtonIcon', () => {
|
||||||
|
it('should render button element correctly', () => {
|
||||||
|
const { getByTestId, container } = render(
|
||||||
|
<ButtonIcon
|
||||||
|
data-testid="button-icon"
|
||||||
|
iconName={IconName.AddSquare}
|
||||||
|
ariaLabel="add"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(container.querySelector('button')).toBeDefined();
|
||||||
|
expect(getByTestId('button-icon')).toHaveClass('mm-button-icon');
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render anchor element correctly', () => {
|
||||||
|
const { getByTestId, container } = render(
|
||||||
|
<ButtonIcon
|
||||||
|
as="a"
|
||||||
|
data-testid="button-icon"
|
||||||
|
iconName={IconName.AddSquare}
|
||||||
|
ariaLabel="add"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(getByTestId('button-icon')).toHaveClass('mm-button-icon');
|
||||||
|
const anchor = container.getElementsByTagName('a').length;
|
||||||
|
expect(anchor).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render anchor element correctly using href', () => {
|
||||||
|
const { getByTestId, getByRole } = render(
|
||||||
|
<ButtonIcon
|
||||||
|
href="/metamask"
|
||||||
|
data-testid="button-icon"
|
||||||
|
iconName={IconName.AddSquare}
|
||||||
|
ariaLabel="add"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(getByTestId('button-icon')).toHaveClass('mm-button-icon');
|
||||||
|
expect(getByRole('link')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render with different size classes', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<>
|
||||||
|
<ButtonIcon
|
||||||
|
iconName={IconName.AddSquare}
|
||||||
|
ariaLabel="add"
|
||||||
|
size={ButtonIconSize.Sm}
|
||||||
|
data-testid={ButtonIconSize.Sm}
|
||||||
|
/>
|
||||||
|
<ButtonIcon
|
||||||
|
iconName={IconName.AddSquare}
|
||||||
|
ariaLabel="add"
|
||||||
|
size={ButtonIconSize.Lg}
|
||||||
|
data-testid={ButtonIconSize.Lg}
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
);
|
||||||
|
expect(getByTestId(ButtonIconSize.Sm)).toHaveClass(
|
||||||
|
`mm-button-icon--size-${ButtonIconSize.Sm}`,
|
||||||
|
);
|
||||||
|
expect(getByTestId(ButtonIconSize.Lg)).toHaveClass(
|
||||||
|
`mm-button-icon--size-${ButtonIconSize.Lg}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render with different colors', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<>
|
||||||
|
<ButtonIcon
|
||||||
|
iconName={IconName.AddSquare}
|
||||||
|
ariaLabel="add"
|
||||||
|
color={IconColor.iconDefault}
|
||||||
|
data-testid={IconColor.iconDefault}
|
||||||
|
/>
|
||||||
|
<ButtonIcon
|
||||||
|
iconName={IconName.AddSquare}
|
||||||
|
ariaLabel="add"
|
||||||
|
color={IconColor.errorDefault}
|
||||||
|
data-testid={IconColor.errorDefault}
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
);
|
||||||
|
expect(getByTestId(IconColor.iconDefault)).toHaveClass(
|
||||||
|
`box--color-${IconColor.iconDefault}`,
|
||||||
|
);
|
||||||
|
expect(getByTestId(IconColor.errorDefault)).toHaveClass(
|
||||||
|
`box--color-${IconColor.errorDefault}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render with added classname', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<ButtonIcon
|
||||||
|
data-testid="classname"
|
||||||
|
className="mm-button-icon--test"
|
||||||
|
iconName={IconName.AddSquare}
|
||||||
|
ariaLabel="add"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(getByTestId('classname')).toHaveClass('mm-button-icon--test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render with different button states', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<>
|
||||||
|
<ButtonIcon
|
||||||
|
disabled
|
||||||
|
data-testid="disabled"
|
||||||
|
iconName={IconName.AddSquare}
|
||||||
|
ariaLabel="add"
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getByTestId('disabled')).toHaveClass(`mm-button-icon--disabled`);
|
||||||
|
expect(getByTestId('disabled')).toBeDisabled();
|
||||||
|
});
|
||||||
|
it('should render with icon', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<ButtonIcon
|
||||||
|
data-testid="icon"
|
||||||
|
iconName={IconName.AddSquare}
|
||||||
|
ariaLabel="add"
|
||||||
|
iconProps={{ 'data-testid': 'button-icon' }}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getByTestId('button-icon')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render with aria-label', () => {
|
||||||
|
const { getByLabelText } = render(
|
||||||
|
<ButtonIcon iconName={IconName.AddSquare} ariaLabel="add" />,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getByLabelText('add')).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
72
ui/components/component-library/button-icon/button-icon.tsx
Normal file
72
ui/components/component-library/button-icon/button-icon.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AlignItems,
|
||||||
|
BackgroundColor,
|
||||||
|
BorderRadius,
|
||||||
|
DISPLAY,
|
||||||
|
IconColor,
|
||||||
|
JustifyContent,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
import Box from '../../ui/box';
|
||||||
|
import { Icon, IconSize } from '../icon';
|
||||||
|
|
||||||
|
import { ButtonIconSize, ButtonIconProps } from './button-icon.types';
|
||||||
|
|
||||||
|
const buttonIconSizeToIconSize: Record<ButtonIconSize, IconSize> = {
|
||||||
|
[ButtonIconSize.Sm]: IconSize.Sm,
|
||||||
|
[ButtonIconSize.Lg]: IconSize.Lg,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ButtonIcon = React.forwardRef(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
ariaLabel,
|
||||||
|
as = 'button',
|
||||||
|
className = '',
|
||||||
|
color = IconColor.iconDefault,
|
||||||
|
href,
|
||||||
|
size = ButtonIconSize.Lg,
|
||||||
|
iconName,
|
||||||
|
disabled,
|
||||||
|
iconProps,
|
||||||
|
...props
|
||||||
|
}: ButtonIconProps,
|
||||||
|
ref: React.Ref<HTMLElement>,
|
||||||
|
) => {
|
||||||
|
const Tag = href ? 'a' : as;
|
||||||
|
const isDisabled = disabled && Tag === 'button';
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
as={Tag}
|
||||||
|
className={classnames(
|
||||||
|
'mm-button-icon',
|
||||||
|
`mm-button-icon--size-${String(size)}`,
|
||||||
|
{
|
||||||
|
'mm-button-icon--disabled': Boolean(disabled),
|
||||||
|
},
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
color={color}
|
||||||
|
{...(isDisabled ? { disabled: true } : {})} // only allow disabled attribute to be passed down to the Box when the as prop is equal to a button element
|
||||||
|
display={DISPLAY.INLINE_FLEX}
|
||||||
|
justifyContent={JustifyContent.center}
|
||||||
|
alignItems={AlignItems.center}
|
||||||
|
borderRadius={BorderRadius.LG}
|
||||||
|
backgroundColor={BackgroundColor.transparent}
|
||||||
|
{...(href ? { href } : {})}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name={iconName}
|
||||||
|
size={buttonIconSizeToIconSize[size]}
|
||||||
|
{...iconProps}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user