mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
Merge branch 'develop' of github.com:MetaMask/metamask-extension into minimal
This commit is contained in:
commit
27e9cb7f88
21
app/_locales/en/messages.json
generated
21
app/_locales/en/messages.json
generated
@ -815,7 +815,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 +830,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"
|
||||||
@ -1808,14 +1808,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 +2486,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 +3331,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"
|
||||||
@ -4660,7 +4663,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 +4749,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": {
|
||||||
|
@ -727,7 +727,6 @@ export function setupController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getUnapprovedTransactionCount() {
|
function getUnapprovedTransactionCount() {
|
||||||
const unapprovedTxCount = controller.txController.getUnapprovedTxCount();
|
|
||||||
const { unapprovedDecryptMsgCount } = controller.decryptMessageManager;
|
const { unapprovedDecryptMsgCount } = controller.decryptMessageManager;
|
||||||
const { unapprovedEncryptionPublicKeyMsgCount } =
|
const { unapprovedEncryptionPublicKeyMsgCount } =
|
||||||
controller.encryptionPublicKeyManager;
|
controller.encryptionPublicKeyManager;
|
||||||
@ -736,7 +735,6 @@ export function setupController(
|
|||||||
const waitingForUnlockCount =
|
const waitingForUnlockCount =
|
||||||
controller.appStateController.waitingForUnlock.length;
|
controller.appStateController.waitingForUnlock.length;
|
||||||
return (
|
return (
|
||||||
unapprovedTxCount +
|
|
||||||
unapprovedDecryptMsgCount +
|
unapprovedDecryptMsgCount +
|
||||||
unapprovedEncryptionPublicKeyMsgCount +
|
unapprovedEncryptionPublicKeyMsgCount +
|
||||||
pendingApprovalCount +
|
pendingApprovalCount +
|
||||||
|
@ -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 = {
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ import sinon from 'sinon';
|
|||||||
import { ControllerMessenger } from '@metamask/base-controller';
|
import { ControllerMessenger } from '@metamask/base-controller';
|
||||||
import { BUILT_IN_NETWORKS } from '../../../../shared/constants/network';
|
import { BUILT_IN_NETWORKS } from '../../../../shared/constants/network';
|
||||||
import { MetaMetricsNetworkEventSource } from '../../../../shared/constants/metametrics';
|
import { MetaMetricsNetworkEventSource } from '../../../../shared/constants/metametrics';
|
||||||
import NetworkController from './network-controller';
|
import { NetworkController } from './network-controller';
|
||||||
|
|
||||||
jest.mock('uuid', () => {
|
jest.mock('uuid', () => {
|
||||||
const actual = jest.requireActual('uuid');
|
const actual = jest.requireActual('uuid');
|
||||||
@ -1100,7 +1100,7 @@ describe('NetworkController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when the request for the latest block responds with null', () => {
|
describe('when the request for the latest block responds with null', () => {
|
||||||
it('stores null as whether the network supports EIP-1559', async () => {
|
it('persists false to state as whether the network supports EIP-1559', async () => {
|
||||||
await withController(
|
await withController(
|
||||||
{
|
{
|
||||||
state: {
|
state: {
|
||||||
@ -1118,13 +1118,13 @@ describe('NetworkController', () => {
|
|||||||
await controller.getEIP1559Compatibility();
|
await controller.getEIP1559Compatibility();
|
||||||
|
|
||||||
expect(controller.store.getState().networkDetails.EIPS[1559]).toBe(
|
expect(controller.store.getState().networkDetails.EIPS[1559]).toBe(
|
||||||
null,
|
false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns null', async () => {
|
it('returns false', async () => {
|
||||||
await withController(async ({ controller, network }) => {
|
await withController(async ({ controller, network }) => {
|
||||||
network.mockEssentialRpcCalls({
|
network.mockEssentialRpcCalls({
|
||||||
latestBlock: null,
|
latestBlock: null,
|
||||||
@ -1133,7 +1133,7 @@ describe('NetworkController', () => {
|
|||||||
|
|
||||||
const supportsEIP1559 = await controller.getEIP1559Compatibility();
|
const supportsEIP1559 = await controller.getEIP1559Compatibility();
|
||||||
|
|
||||||
expect(supportsEIP1559).toBe(null);
|
expect(supportsEIP1559).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
1171
app/scripts/controllers/network/network-controller.ts
Normal file
1171
app/scripts/controllers/network/network-controller.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||||
|
@ -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')
|
txController.setTxHash('1', originalValue);
|
||||||
.callsFake(() => {
|
txController.txStateManager.setTxStatusSubmitted('1');
|
||||||
txController.setTxHash('1', originalValue);
|
});
|
||||||
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 () {
|
||||||
|
@ -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';
|
||||||
@ -263,7 +264,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
|
|
||||||
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 +311,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 +453,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 +492,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(
|
||||||
@ -609,7 +610,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 +622,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 +1008,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 +1107,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 {
|
||||||
@ -1145,7 +1153,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 +1196,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,7 +1204,7 @@ 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.encryptionPublicKeyManager.clearUnapproved();
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -87,6 +87,7 @@ 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';
|
||||||
|
|
||||||
const migrations = [
|
const migrations = [
|
||||||
m002,
|
m002,
|
||||||
@ -171,6 +172,7 @@ const migrations = [
|
|||||||
m081,
|
m081,
|
||||||
m082,
|
m082,
|
||||||
m083,
|
m083,
|
||||||
|
m084,
|
||||||
];
|
];
|
||||||
|
|
||||||
export default migrations;
|
export default migrations;
|
||||||
|
@ -51,7 +51,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
|
||||||
|
@ -1193,7 +1193,7 @@
|
|||||||
"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/gas-fee-controller>@metamask/controller-utils": true,
|
||||||
"eth-query": true,
|
"eth-query": true,
|
||||||
"ethereumjs-util": true,
|
"ethereumjs-util": true,
|
||||||
@ -1201,11 +1201,6 @@
|
|||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"console.error": true,
|
"console.error": true,
|
||||||
|
@ -1265,7 +1265,7 @@
|
|||||||
"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/gas-fee-controller>@metamask/controller-utils": true,
|
||||||
"eth-query": true,
|
"eth-query": true,
|
||||||
"ethereumjs-util": true,
|
"ethereumjs-util": true,
|
||||||
@ -1273,11 +1273,6 @@
|
|||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"console.error": true,
|
"console.error": true,
|
||||||
|
@ -1265,7 +1265,7 @@
|
|||||||
"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/gas-fee-controller>@metamask/controller-utils": true,
|
||||||
"eth-query": true,
|
"eth-query": true,
|
||||||
"ethereumjs-util": true,
|
"ethereumjs-util": true,
|
||||||
@ -1273,11 +1273,6 @@
|
|||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"console.error": true,
|
"console.error": true,
|
||||||
|
@ -1193,7 +1193,7 @@
|
|||||||
"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/gas-fee-controller>@metamask/controller-utils": true,
|
||||||
"eth-query": true,
|
"eth-query": true,
|
||||||
"ethereumjs-util": true,
|
"ethereumjs-util": true,
|
||||||
@ -1201,11 +1201,6 @@
|
|||||||
"uuid": true
|
"uuid": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@metamask/gas-fee-controller>@metamask/base-controller": {
|
|
||||||
"packages": {
|
|
||||||
"immer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
"@metamask/gas-fee-controller>@metamask/controller-utils": {
|
||||||
"globals": {
|
"globals": {
|
||||||
"console.error": true,
|
"console.error": true,
|
||||||
|
@ -242,14 +242,14 @@
|
|||||||
"@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": "^3.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": "^2.1.0",
|
||||||
"@metamask/metamask-eth-abis": "^3.0.0",
|
"@metamask/metamask-eth-abis": "^3.0.0",
|
||||||
"@metamask/notification-controller": "^1.0.0",
|
"@metamask/notification-controller": "^1.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": "^2.0.0",
|
||||||
"@metamask/post-message-stream": "^6.0.0",
|
"@metamask/post-message-stream": "^6.0.0",
|
||||||
|
@ -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',
|
||||||
|
@ -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,
|
||||||
|
@ -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",
|
||||||
|
@ -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');
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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',
|
||||||
|
@ -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' });
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -152,7 +152,7 @@ export function useTransactionDisplayData(transactionGroup) {
|
|||||||
async function getAndSetAssetDetails() {
|
async function getAndSetAssetDetails() {
|
||||||
if (isTokenCategory && !token) {
|
if (isTokenCategory && !token) {
|
||||||
const assetDetails = await getAssetDetails(
|
const assetDetails = await getAssetDetails(
|
||||||
recipientAddress,
|
to,
|
||||||
senderAddress,
|
senderAddress,
|
||||||
initialTransaction?.txParams?.data,
|
initialTransaction?.txParams?.data,
|
||||||
knownNfts,
|
knownNfts,
|
||||||
@ -168,6 +168,7 @@ export function useTransactionDisplayData(transactionGroup) {
|
|||||||
senderAddress,
|
senderAddress,
|
||||||
initialTransaction?.txParams?.data,
|
initialTransaction?.txParams?.data,
|
||||||
knownNfts,
|
knownNfts,
|
||||||
|
to,
|
||||||
]);
|
]);
|
||||||
if (currentAssetDetails) {
|
if (currentAssetDetails) {
|
||||||
token = {
|
token = {
|
||||||
|
@ -57,7 +57,7 @@ exports[`ConfirmApproveContent Component should render Confirm approve page corr
|
|||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
Verify contract details
|
Verify third-party details
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -244,7 +244,7 @@ exports[`ConfirmApproveContent Component should render Confirm approve page corr
|
|||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
Verify contract details
|
Verify third-party details
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -431,7 +431,7 @@ exports[`ConfirmApproveContent Component should render Confirm approve page corr
|
|||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
Verify contract details
|
Verify third-party details
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -618,7 +618,7 @@ exports[`ConfirmApproveContent Component should render Confirm approve page corr
|
|||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
Verify contract details
|
Verify third-party details
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -56,7 +56,7 @@ describe('ConfirmApproveContent Component', () => {
|
|||||||
'This allows a third party to access and transfer the following NFTs without further notice until you revoke its access.',
|
'This allows a third party to access and transfer the following NFTs without further notice until you revoke its access.',
|
||||||
),
|
),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(queryByText('Verify contract details')).toBeInTheDocument();
|
expect(queryByText('Verify third-party details')).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
queryByText(
|
queryByText(
|
||||||
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
|
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
|
||||||
@ -119,7 +119,7 @@ describe('ConfirmApproveContent Component', () => {
|
|||||||
'This allows a third party to access and transfer the following NFTs without further notice until you revoke its access.',
|
'This allows a third party to access and transfer the following NFTs without further notice until you revoke its access.',
|
||||||
),
|
),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(queryByText('Verify contract details')).toBeInTheDocument();
|
expect(queryByText('Verify third-party details')).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
queryByText(
|
queryByText(
|
||||||
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
|
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
|
||||||
@ -181,7 +181,7 @@ describe('ConfirmApproveContent Component', () => {
|
|||||||
'This allows a third party to access and transfer the following NFTs without further notice until you revoke its access.',
|
'This allows a third party to access and transfer the following NFTs without further notice until you revoke its access.',
|
||||||
),
|
),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(queryByText('Verify contract details')).toBeInTheDocument();
|
expect(queryByText('Verify third-party details')).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
queryByText(
|
queryByText(
|
||||||
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
|
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
|
||||||
@ -239,7 +239,7 @@ describe('ConfirmApproveContent Component', () => {
|
|||||||
'This allows a third party to access and transfer the following NFTs without further notice until you revoke its access.',
|
'This allows a third party to access and transfer the following NFTs without further notice until you revoke its access.',
|
||||||
),
|
),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(queryByText('Verify contract details')).toBeInTheDocument();
|
expect(queryByText('Verify third-party details')).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
queryByText(
|
queryByText(
|
||||||
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
|
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.',
|
||||||
|
@ -223,7 +223,7 @@ exports[`Signature Request Component render should match snapshot 1`] = `
|
|||||||
<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>
|
||||||
|
@ -5,10 +5,10 @@ import UrlIcon from '../../../components/ui/url-icon';
|
|||||||
import Popover from '../../../components/ui/popover';
|
import Popover from '../../../components/ui/popover';
|
||||||
import Button from '../../../components/ui/button';
|
import Button from '../../../components/ui/button';
|
||||||
import Box from '../../../components/ui/box';
|
import Box from '../../../components/ui/box';
|
||||||
import Typography from '../../../components/ui/typography';
|
import { Text } from '../../../components/component-library';
|
||||||
import ActionableMessage from '../../../components/ui/actionable-message/actionable-message';
|
import ActionableMessage from '../../../components/ui/actionable-message/actionable-message';
|
||||||
import {
|
import {
|
||||||
TypographyVariant,
|
TextVariant,
|
||||||
FONT_WEIGHT,
|
FONT_WEIGHT,
|
||||||
AlignItems,
|
AlignItems,
|
||||||
DISPLAY,
|
DISPLAY,
|
||||||
@ -62,21 +62,26 @@ export default function ImportToken({
|
|||||||
fallbackClassName="import-token__token-icon"
|
fallbackClassName="import-token__token-icon"
|
||||||
name={tokenForImport.symbol}
|
name={tokenForImport.symbol}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Text
|
||||||
ariant={TypographyVariant.H4}
|
variant={TextVariant.headingSm}
|
||||||
|
as="h4"
|
||||||
fontWeight={FONT_WEIGHT.BOLD}
|
fontWeight={FONT_WEIGHT.BOLD}
|
||||||
boxProps={{ marginTop: 2, marginBottom: 3 }}
|
marginTop={2}
|
||||||
|
marginBottom={3}
|
||||||
>
|
>
|
||||||
{tokenForImport.name || ''}
|
{tokenForImport.name || ''}
|
||||||
</Typography>
|
</Text>
|
||||||
<Typography variant={TypographyVariant.H6}>{t('contract')}:</Typography>
|
<Text variant={TextVariant.bodySm} as="h6">
|
||||||
<Typography
|
{t('contract')}:
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
variant={TextVariant.bodySm}
|
||||||
className="import-token__contract-address"
|
className="import-token__contract-address"
|
||||||
variant={TypographyVariant.H7}
|
as="h6"
|
||||||
boxProps={{ marginBottom: 6 }}
|
marginBottom={6}
|
||||||
>
|
>
|
||||||
{tokenForImport.address || ''}
|
{tokenForImport.address || ''}
|
||||||
</Typography>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: var(--color-background-alternative);
|
background-color: var(--color-background-alternative);
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__token-icon {
|
&__token-icon {
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
a.token-allowance-container__verify-link {
|
a.token-allowance-container__verify-link {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
margin-inline-start: 96px;
|
margin-inline-start: auto;
|
||||||
margin-inline-end: 96px;
|
margin-inline-end: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,16 +237,16 @@ describe('TokenAllowancePage', () => {
|
|||||||
expect(getByText('Set a spending cap for your')).toBeInTheDocument();
|
expect(getByText('Set a spending cap for your')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should click Verify contract details and show popup Contract details, then close popup', () => {
|
it('should click Verify third-party details and show popup Third-party details, then close popup', () => {
|
||||||
const { getByText } = renderWithProvider(
|
const { getByText } = renderWithProvider(
|
||||||
<TokenAllowance {...props} />,
|
<TokenAllowance {...props} />,
|
||||||
store,
|
store,
|
||||||
);
|
);
|
||||||
|
|
||||||
const verifyContractDetails = getByText('Verify contract details');
|
const verifyThirdPartyDetails = getByText('Verify third-party details');
|
||||||
fireEvent.click(verifyContractDetails);
|
fireEvent.click(verifyThirdPartyDetails);
|
||||||
|
|
||||||
expect(getByText('Contract details')).toBeInTheDocument();
|
expect(getByText('Third-party details')).toBeInTheDocument();
|
||||||
|
|
||||||
const gotIt = getByText('Got it');
|
const gotIt = getByText('Got it');
|
||||||
fireEvent.click(gotIt);
|
fireEvent.click(gotIt);
|
||||||
|
@ -484,21 +484,14 @@ export function getCurrentCurrency(state) {
|
|||||||
|
|
||||||
export function getTotalUnapprovedCount(state) {
|
export function getTotalUnapprovedCount(state) {
|
||||||
const {
|
const {
|
||||||
unapprovedMsgCount = 0,
|
|
||||||
unapprovedPersonalMsgCount = 0,
|
|
||||||
unapprovedDecryptMsgCount = 0,
|
unapprovedDecryptMsgCount = 0,
|
||||||
unapprovedEncryptionPublicKeyMsgCount = 0,
|
unapprovedEncryptionPublicKeyMsgCount = 0,
|
||||||
unapprovedTypedMessagesCount = 0,
|
|
||||||
pendingApprovalCount = 0,
|
pendingApprovalCount = 0,
|
||||||
} = state.metamask;
|
} = state.metamask;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
unapprovedMsgCount +
|
|
||||||
unapprovedPersonalMsgCount +
|
|
||||||
unapprovedDecryptMsgCount +
|
unapprovedDecryptMsgCount +
|
||||||
unapprovedEncryptionPublicKeyMsgCount +
|
unapprovedEncryptionPublicKeyMsgCount +
|
||||||
unapprovedTypedMessagesCount +
|
|
||||||
getUnapprovedTxCount(state) +
|
|
||||||
pendingApprovalCount +
|
pendingApprovalCount +
|
||||||
getSuggestedAssetCount(state)
|
getSuggestedAssetCount(state)
|
||||||
);
|
);
|
||||||
|
66
yarn.lock
66
yarn.lock
@ -3984,13 +3984,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@metamask/gas-fee-controller@npm:^1.0.0":
|
"@metamask/gas-fee-controller@npm:^3.0.0":
|
||||||
version: 1.0.0
|
version: 3.0.0
|
||||||
resolution: "@metamask/gas-fee-controller@npm:1.0.0"
|
resolution: "@metamask/gas-fee-controller@npm:3.0.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@metamask/base-controller": ~1.0.0
|
"@metamask/base-controller": ^1.1.2
|
||||||
"@metamask/controller-utils": ~1.0.0
|
"@metamask/controller-utils": ^2.0.0
|
||||||
"@metamask/network-controller": ~1.0.0
|
"@metamask/network-controller": ^3.0.0
|
||||||
"@types/uuid": ^8.3.0
|
"@types/uuid": ^8.3.0
|
||||||
babel-runtime: ^6.26.0
|
babel-runtime: ^6.26.0
|
||||||
eth-query: ^2.1.2
|
eth-query: ^2.1.2
|
||||||
@ -3998,7 +3998,9 @@ __metadata:
|
|||||||
ethjs-unit: ^0.1.6
|
ethjs-unit: ^0.1.6
|
||||||
immer: ^9.0.6
|
immer: ^9.0.6
|
||||||
uuid: ^8.3.2
|
uuid: ^8.3.2
|
||||||
checksum: fef5255532a6cd5325ddfbbfec11140e6629c011a8cc6b126672ef7a6e93a327d059935cdc6fc7089562f3277fb70541b5ea54cd31c0e5b350ceebbe73d5d59f
|
peerDependencies:
|
||||||
|
"@metamask/network-controller": ^3.0.0
|
||||||
|
checksum: 8cdd43a265094dd5e41f0094c278cde351d290446711e6b39de26f842faa993c050e5506cafe8d1c2fb0c4ee3f0f97c5af5fa6528de10e76d071b56fb9673da8
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -4074,6 +4076,22 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@metamask/network-controller@npm:^3.0.0":
|
||||||
|
version: 3.0.0
|
||||||
|
resolution: "@metamask/network-controller@npm:3.0.0"
|
||||||
|
dependencies:
|
||||||
|
"@metamask/base-controller": ^1.1.2
|
||||||
|
"@metamask/controller-utils": ^2.0.0
|
||||||
|
async-mutex: ^0.2.6
|
||||||
|
babel-runtime: ^6.26.0
|
||||||
|
eth-json-rpc-infura: ^5.1.0
|
||||||
|
eth-query: ^2.1.2
|
||||||
|
immer: ^9.0.6
|
||||||
|
web3-provider-engine: ^16.0.3
|
||||||
|
checksum: 3ae56a252c11dbd6dc843f9db8b30768d2475afd499c99bdccdc850517031b447bab9ca4f6647da7e64c7a0efd61d029f59a89e4ec702e34a99733dd8e7f93ff
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@metamask/network-controller@npm:^4.0.0":
|
"@metamask/network-controller@npm:^4.0.0":
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
resolution: "@metamask/network-controller@npm:4.0.0"
|
resolution: "@metamask/network-controller@npm:4.0.0"
|
||||||
@ -4090,22 +4108,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@metamask/network-controller@npm:~1.0.0":
|
|
||||||
version: 1.0.0
|
|
||||||
resolution: "@metamask/network-controller@npm:1.0.0"
|
|
||||||
dependencies:
|
|
||||||
"@metamask/base-controller": ~1.0.0
|
|
||||||
"@metamask/controller-utils": ~1.0.0
|
|
||||||
async-mutex: ^0.2.6
|
|
||||||
babel-runtime: ^6.26.0
|
|
||||||
eth-json-rpc-infura: ^5.1.0
|
|
||||||
eth-query: ^2.1.2
|
|
||||||
immer: ^9.0.6
|
|
||||||
web3-provider-engine: ^16.0.3
|
|
||||||
checksum: a138943fecc27630e6fe392b9d237405e61b55e17b9dcfc7c434ccc59582fc775aec54e765c2e98f2b1579f760c7d163156450184172128079ce3c4d8e4bc725
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@metamask/notification-controller@npm:^1.0.0":
|
"@metamask/notification-controller@npm:^1.0.0":
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
resolution: "@metamask/notification-controller@npm:1.0.0"
|
resolution: "@metamask/notification-controller@npm:1.0.0"
|
||||||
@ -4150,13 +4152,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@metamask/obs-store@npm:^8.0.0":
|
"@metamask/obs-store@npm:^8.1.0":
|
||||||
version: 8.0.0
|
version: 8.1.0
|
||||||
resolution: "@metamask/obs-store@npm:8.0.0"
|
resolution: "@metamask/obs-store@npm:8.1.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@metamask/safe-event-emitter": ^2.0.0
|
"@metamask/safe-event-emitter": ^2.0.0
|
||||||
through2: ^2.0.3
|
through2: ^2.0.3
|
||||||
checksum: 232362e65a3563f0bd3299cec48f5adb37e68d4f066b7de90f2b044480d3b16c2d918c12d672c825e1d9b55344ae818fb8494d91129e4613555097653b9bb887
|
checksum: 92356067fa3517526d656f2f0bdfbc4d39f65e27fb30d84240cfc9c1aa9cd5d743498952df18ed8efbb8887b6cc1bc1fab37bde3fb0fc059539e0dfcc67ff86f
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -24299,14 +24301,14 @@ __metadata:
|
|||||||
"@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/forwarder": ^1.1.0
|
"@metamask/forwarder": ^1.1.0
|
||||||
"@metamask/gas-fee-controller": ^1.0.0
|
"@metamask/gas-fee-controller": ^3.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": ^2.1.0
|
||||||
"@metamask/metamask-eth-abis": ^3.0.0
|
"@metamask/metamask-eth-abis": ^3.0.0
|
||||||
"@metamask/notification-controller": ^1.0.0
|
"@metamask/notification-controller": ^1.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": ^2.0.0
|
||||||
"@metamask/phishing-warning": ^2.1.0
|
"@metamask/phishing-warning": ^2.1.0
|
||||||
@ -34616,14 +34618,14 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"vm2@npm:^3.9.3":
|
"vm2@npm:^3.9.3":
|
||||||
version: 3.9.11
|
version: 3.9.15
|
||||||
resolution: "vm2@npm:3.9.11"
|
resolution: "vm2@npm:3.9.15"
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: ^8.7.0
|
acorn: ^8.7.0
|
||||||
acorn-walk: ^8.2.0
|
acorn-walk: ^8.2.0
|
||||||
bin:
|
bin:
|
||||||
vm2: bin/vm2
|
vm2: bin/vm2
|
||||||
checksum: aab39e6e4b59146d24abacd79f490e854a6e058a8b23d93d2be5aca7720778e2605d2cc028ccc4a5f50d3d91b0c38be9a6247a80d2da1a6de09425cc437770b4
|
checksum: 1df70d5a88173651c0062901aba67e5edfeeb3f699fe6c305f5efb6a5a7391e5724cbf98a6516600b65016c6824dc07cc79947ea4222f8537ae1d9ce0b730ad7
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user