mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
NetworkController: Split network
into networkId
and networkStatus
(#17556)
The `network` store of the network controller crams two types of data into one place. It roughly tracks whether we have enough information to make requests to the network and whether the network is capable of receiving requests, but it also stores the ID of the network (as obtained via `net_version`). Generally we shouldn't be using the network ID for anything, as it has been completely replaced by chain ID, which all custom RPC endpoints have been required to support for over a year now. However, as the network ID is used in various places within the extension codebase, removing it entirely would be a non-trivial effort. So, minimally, this commit splits `network` into two stores: `networkId` and `networkStatus`. But it also expands the concept of network status. Previously, the network was in one of two states: "loading" and "not-loading". But now it can be in one of four states: - `available`: The network is able to receive and respond to requests. - `unavailable`: The network is not able to receive and respond to requests for unknown reasons. - `blocked`: The network is actively blocking requests based on the user's geolocation. (This is specific to Infura.) - `unknown`: We don't know whether the network can receive and respond to requests, either because we haven't checked or we tried to check and were unsuccessful. This commit also changes how the network status is determined — specifically, how many requests are used to determine that status, when they occur, and whether they are awaited. Previously, the network controller would make 2 to 3 requests during the course of running `lookupNetwork`. * First, if it was an Infura network, it would make a request for `eth_blockNumber` to determine whether Infura was blocking requests or not, then emit an appropriate event. This operation was not awaited. * Then, regardless of the network, it would fetch the network ID via `net_version`. This operation was awaited. * Finally, regardless of the network, it would fetch the latest block via `eth_getBlockByNumber`, then use the result to determine whether the network supported EIP-1559. This operation was awaited. Now: * One fewer request is made, specifically `eth_blockNumber`, as we don't need to make an extra request to determine whether Infura is blocking requests; we can reuse `eth_getBlockByNumber`; * All requests are awaited, which makes `lookupNetwork` run fully in-band instead of partially out-of-band; and * Both requests for `net_version` and `eth_getBlockByNumber` are performed in parallel to make `lookupNetwork` run slightly faster.
This commit is contained in:
parent
a53b9fb489
commit
ed3cc404f2
@ -223,7 +223,8 @@ browser.runtime.onConnectExternal.addListener(async (...args) => {
|
||||
* @property {object} provider - The current selected network provider.
|
||||
* @property {string} provider.rpcUrl - The address for the RPC API, if using an RPC API.
|
||||
* @property {string} provider.type - An identifier for the type of network selected, allows MetaMask to use custom provider strategies for known networks.
|
||||
* @property {string} network - A stringified number of the current network ID.
|
||||
* @property {string} networkId - The stringified number of the current network ID.
|
||||
* @property {string} networkStatus - Either "unknown", "available", "unavailable", or "blocked", depending on the status of the currently selected network.
|
||||
* @property {object} accounts - An object mapping lower-case hex addresses to objects with "balance" and "address" keys, both storing hex string values.
|
||||
* @property {hex} currentBlockGasLimit - The most recently seen block gas limit, in a lower case hex prefixed string.
|
||||
* @property {TransactionMeta[]} currentNetworkTxList - An array of transactions associated with the currently selected network.
|
||||
|
@ -17,8 +17,8 @@ import NetworkController, { NetworkControllerEventTypes } from './network';
|
||||
import PreferencesController from './preferences';
|
||||
|
||||
describe('DetectTokensController', function () {
|
||||
const sandbox = sinon.createSandbox();
|
||||
let assetsContractController,
|
||||
let sandbox,
|
||||
assetsContractController,
|
||||
keyringMemStore,
|
||||
network,
|
||||
preferences,
|
||||
@ -32,87 +32,94 @@ describe('DetectTokensController', function () {
|
||||
getAccounts: noop,
|
||||
};
|
||||
|
||||
const infuraProjectId = 'infura-project-id';
|
||||
|
||||
beforeEach(async function () {
|
||||
keyringMemStore = new ObservableStore({ isUnlocked: false });
|
||||
const networkControllerMessenger = new ControllerMessenger();
|
||||
network = new NetworkController({
|
||||
messenger: networkControllerMessenger,
|
||||
infuraProjectId: 'foo',
|
||||
});
|
||||
network.initializeProvider(networkControllerProviderConfig);
|
||||
provider = network.getProviderAndBlockTracker().provider;
|
||||
|
||||
const tokenListMessenger = new ControllerMessenger().getRestricted({
|
||||
name: 'TokenListController',
|
||||
});
|
||||
tokenListController = new TokenListController({
|
||||
chainId: '1',
|
||||
preventPollingOnNetworkRestart: false,
|
||||
onNetworkStateChange: sinon.spy(),
|
||||
onPreferencesStateChange: sinon.spy(),
|
||||
messenger: tokenListMessenger,
|
||||
});
|
||||
await tokenListController.start();
|
||||
|
||||
preferences = new PreferencesController({
|
||||
network,
|
||||
provider,
|
||||
tokenListController,
|
||||
onInfuraIsBlocked: sinon.stub(),
|
||||
onInfuraIsUnblocked: sinon.stub(),
|
||||
});
|
||||
preferences.setAddresses([
|
||||
'0x7e57e2',
|
||||
'0xbc86727e770de68b1060c91f6bb6945c73e10388',
|
||||
]);
|
||||
preferences.setUseTokenDetection(true);
|
||||
|
||||
tokensController = new TokensController({
|
||||
onPreferencesStateChange: preferences.store.subscribe.bind(
|
||||
preferences.store,
|
||||
),
|
||||
onNetworkStateChange: (cb) =>
|
||||
network.store.subscribe((networkState) => {
|
||||
const modifiedNetworkState = {
|
||||
...networkState,
|
||||
providerConfig: {
|
||||
...networkState.provider,
|
||||
sandbox = sinon.createSandbox();
|
||||
// Disable all requests, even those to localhost
|
||||
nock.disableNetConnect();
|
||||
nock('https://mainnet.infura.io')
|
||||
.post(`/v3/${infuraProjectId}`)
|
||||
.reply(200, (_uri, requestBody) => {
|
||||
if (requestBody.method === 'eth_getBlockByNumber') {
|
||||
return {
|
||||
id: requestBody.id,
|
||||
jsonrpc: '2.0',
|
||||
result: {
|
||||
number: '0x42',
|
||||
},
|
||||
};
|
||||
return cb(modifiedNetworkState);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
assetsContractController = new AssetsContractController({
|
||||
onPreferencesStateChange: preferences.store.subscribe.bind(
|
||||
preferences.store,
|
||||
),
|
||||
onNetworkStateChange: (cb) =>
|
||||
networkControllerMessenger.subscribe(
|
||||
NetworkControllerEventTypes.NetworkDidChange,
|
||||
() => {
|
||||
const networkState = network.store.getState();
|
||||
const modifiedNetworkState = {
|
||||
...networkState,
|
||||
providerConfig: {
|
||||
...networkState.provider,
|
||||
chainId: convertHexToDecimal(networkState.provider.chainId),
|
||||
if (requestBody.method === 'eth_blockNumber') {
|
||||
return {
|
||||
id: requestBody.id,
|
||||
jsonrpc: '2.0',
|
||||
result: '0x42',
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`(Infura) Mock not defined for ${requestBody.method}`);
|
||||
})
|
||||
.persist();
|
||||
nock('https://sepolia.infura.io')
|
||||
.post(`/v3/${infuraProjectId}`)
|
||||
.reply(200, (_uri, requestBody) => {
|
||||
if (requestBody.method === 'eth_getBlockByNumber') {
|
||||
return {
|
||||
id: requestBody.id,
|
||||
jsonrpc: '2.0',
|
||||
result: {
|
||||
number: '0x42',
|
||||
},
|
||||
};
|
||||
return cb(modifiedNetworkState);
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
sandbox
|
||||
.stub(network, '_getLatestBlock')
|
||||
.callsFake(() => Promise.resolve({}));
|
||||
sandbox
|
||||
.stub(tokensController, '_instantiateNewEthersProvider')
|
||||
.returns(null);
|
||||
sandbox
|
||||
.stub(tokensController, '_detectIsERC721')
|
||||
.returns(Promise.resolve(false));
|
||||
if (requestBody.method === 'eth_blockNumber') {
|
||||
return {
|
||||
id: requestBody.id,
|
||||
jsonrpc: '2.0',
|
||||
result: '0x42',
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`(Infura) Mock not defined for ${requestBody.method}`);
|
||||
})
|
||||
.persist();
|
||||
nock('http://localhost:8545')
|
||||
.post('/')
|
||||
.reply(200, (_uri, requestBody) => {
|
||||
if (requestBody.method === 'eth_getBlockByNumber') {
|
||||
return {
|
||||
id: requestBody.id,
|
||||
jsonrpc: '2.0',
|
||||
result: {
|
||||
number: '0x42',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (requestBody.method === 'eth_blockNumber') {
|
||||
return {
|
||||
id: requestBody.id,
|
||||
jsonrpc: '2.0',
|
||||
result: '0x42',
|
||||
};
|
||||
}
|
||||
|
||||
if (requestBody.method === 'net_version') {
|
||||
return {
|
||||
id: requestBody.id,
|
||||
jsonrpc: '2.0',
|
||||
result: '1337',
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`(localhost) Mock not defined for ${requestBody.method}`,
|
||||
);
|
||||
})
|
||||
.persist();
|
||||
nock('https://token-api.metaswap.codefi.network')
|
||||
.get(`/tokens/1`)
|
||||
.reply(200, [
|
||||
@ -183,9 +190,82 @@ describe('DetectTokensController', function () {
|
||||
.get(`/tokens/3`)
|
||||
.reply(200, { error: 'ChainId 3 is not supported' })
|
||||
.persist();
|
||||
|
||||
keyringMemStore = new ObservableStore({ isUnlocked: false });
|
||||
const networkControllerMessenger = new ControllerMessenger();
|
||||
network = new NetworkController({
|
||||
messenger: networkControllerMessenger,
|
||||
infuraProjectId,
|
||||
});
|
||||
await network.initializeProvider(networkControllerProviderConfig);
|
||||
provider = network.getProviderAndBlockTracker().provider;
|
||||
|
||||
const tokenListMessenger = new ControllerMessenger().getRestricted({
|
||||
name: 'TokenListController',
|
||||
});
|
||||
tokenListController = new TokenListController({
|
||||
chainId: '1',
|
||||
preventPollingOnNetworkRestart: false,
|
||||
onNetworkStateChange: sinon.spy(),
|
||||
onPreferencesStateChange: sinon.spy(),
|
||||
messenger: tokenListMessenger,
|
||||
});
|
||||
await tokenListController.start();
|
||||
|
||||
preferences = new PreferencesController({
|
||||
network,
|
||||
provider,
|
||||
tokenListController,
|
||||
onInfuraIsBlocked: sinon.stub(),
|
||||
onInfuraIsUnblocked: sinon.stub(),
|
||||
});
|
||||
preferences.setAddresses([
|
||||
'0x7e57e2',
|
||||
'0xbc86727e770de68b1060c91f6bb6945c73e10388',
|
||||
]);
|
||||
preferences.setUseTokenDetection(true);
|
||||
|
||||
tokensController = new TokensController({
|
||||
config: { provider },
|
||||
onPreferencesStateChange: preferences.store.subscribe.bind(
|
||||
preferences.store,
|
||||
),
|
||||
onNetworkStateChange: (cb) =>
|
||||
network.store.subscribe((networkState) => {
|
||||
const modifiedNetworkState = {
|
||||
...networkState,
|
||||
providerConfig: {
|
||||
...networkState.provider,
|
||||
},
|
||||
};
|
||||
return cb(modifiedNetworkState);
|
||||
}),
|
||||
});
|
||||
|
||||
after(function () {
|
||||
assetsContractController = new AssetsContractController({
|
||||
onPreferencesStateChange: preferences.store.subscribe.bind(
|
||||
preferences.store,
|
||||
),
|
||||
onNetworkStateChange: (cb) =>
|
||||
networkControllerMessenger.subscribe(
|
||||
NetworkControllerEventTypes.NetworkDidChange,
|
||||
() => {
|
||||
const networkState = network.store.getState();
|
||||
const modifiedNetworkState = {
|
||||
...networkState,
|
||||
providerConfig: {
|
||||
...networkState.provider,
|
||||
chainId: convertHexToDecimal(networkState.provider.chainId),
|
||||
},
|
||||
};
|
||||
return cb(modifiedNetworkState);
|
||||
},
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
nock.enableNetConnect('localhost');
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
|
@ -11,6 +11,8 @@ import EthQuery from 'eth-query';
|
||||
// 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,
|
||||
@ -18,8 +20,8 @@ import {
|
||||
TEST_NETWORK_TICKER_MAP,
|
||||
CHAIN_IDS,
|
||||
NETWORK_TYPES,
|
||||
NetworkStatus,
|
||||
} from '../../../../shared/constants/network';
|
||||
import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout';
|
||||
import {
|
||||
isPrefixedFormattedHexString,
|
||||
isSafeChainId,
|
||||
@ -36,40 +38,57 @@ import { createNetworkClient } from './create-network-client';
|
||||
* @property {string} [nickname] - Personalized network name.
|
||||
*/
|
||||
|
||||
const env = process.env.METAMASK_ENV;
|
||||
const fetchWithTimeout = getFetchWithTimeout();
|
||||
|
||||
const name = 'NetworkController';
|
||||
|
||||
let defaultProviderConfigOpts;
|
||||
if (process.env.IN_TEST) {
|
||||
defaultProviderConfigOpts = {
|
||||
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 || env === 'test') {
|
||||
defaultProviderConfigOpts = {
|
||||
} 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,
|
||||
};
|
||||
} else {
|
||||
defaultProviderConfigOpts = {
|
||||
}
|
||||
|
||||
return {
|
||||
type: NETWORK_TYPES.MAINNET,
|
||||
chainId: CHAIN_IDS.MAINNET,
|
||||
ticker: 'ETH',
|
||||
};
|
||||
}
|
||||
|
||||
const defaultProviderConfig = {
|
||||
ticker: 'ETH',
|
||||
...defaultProviderConfigOpts,
|
||||
};
|
||||
function buildDefaultNetworkIdState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const defaultNetworkDetailsState = {
|
||||
EIPS: { 1559: undefined },
|
||||
};
|
||||
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.
|
||||
@ -98,8 +117,6 @@ export const NetworkControllerEventTypes = {
|
||||
};
|
||||
|
||||
export default class NetworkController extends EventEmitter {
|
||||
static defaultProviderConfig = defaultProviderConfig;
|
||||
|
||||
/**
|
||||
* Construct a NetworkController.
|
||||
*
|
||||
@ -121,31 +138,33 @@ export default class NetworkController extends EventEmitter {
|
||||
|
||||
// create stores
|
||||
this.providerStore = new ObservableStore(
|
||||
state.provider || { ...defaultProviderConfig },
|
||||
state.provider || buildDefaultProviderConfigState(),
|
||||
);
|
||||
this.previousProviderStore = new ObservableStore(
|
||||
this.providerStore.getState(),
|
||||
);
|
||||
this.networkStore = new ObservableStore('loading');
|
||||
// We need to keep track of a few details about the current network
|
||||
// Ideally we'd merge this.networkStore 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.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 || {
|
||||
...defaultNetworkDetailsState,
|
||||
},
|
||||
state.networkDetails || buildDefaultNetworkDetailsState(),
|
||||
);
|
||||
|
||||
this.networkConfigurationsStore = new ObservableStore(
|
||||
state.networkConfigurations || {},
|
||||
state.networkConfigurations || buildDefaultNetworkConfigurationsState(),
|
||||
);
|
||||
|
||||
this.store = new ComposedStore({
|
||||
provider: this.providerStore,
|
||||
previousProviderStore: this.previousProviderStore,
|
||||
network: this.networkStore,
|
||||
networkId: this.networkIdStore,
|
||||
networkStatus: this.networkStatusStore,
|
||||
networkDetails: this.networkDetails,
|
||||
networkConfigurations: this.networkConfigurationsStore,
|
||||
});
|
||||
@ -189,10 +208,12 @@ export default class NetworkController extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to check if the block header contains fields that indicate EIP 1559
|
||||
* support (baseFeePerGas).
|
||||
* Determines whether the network supports EIP-1559 by checking whether the
|
||||
* latest block has a `baseFeePerGas` property, then updates state
|
||||
* appropriately.
|
||||
*
|
||||
* @returns {Promise<boolean>} true if current network supports EIP 1559
|
||||
* @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();
|
||||
@ -201,15 +222,28 @@ export default class NetworkController extends EventEmitter {
|
||||
if (EIPS[1559] !== undefined) {
|
||||
return EIPS[1559];
|
||||
}
|
||||
const latestBlock = await this._getLatestBlock();
|
||||
const supportsEIP1559 =
|
||||
latestBlock && latestBlock.baseFeePerGas !== undefined;
|
||||
this._setNetworkEIPSupport(1559, supportsEIP1559);
|
||||
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() {
|
||||
// Prevent firing when provider is not defined.
|
||||
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',
|
||||
@ -217,46 +251,102 @@ export default class NetworkController extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
const { chainId } = this.providerStore.getState();
|
||||
if (!chainId) {
|
||||
log.warn(
|
||||
'NetworkController - lookupNetwork aborted due to missing chainId',
|
||||
);
|
||||
this._setNetworkState('loading');
|
||||
this._clearNetworkDetails();
|
||||
this._resetNetworkId();
|
||||
this._resetNetworkStatus();
|
||||
this._resetNetworkDetails();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ping the RPC endpoint so we can confirm that it works
|
||||
const initialNetwork = this.networkStore.getState();
|
||||
const { type } = this.providerStore.getState();
|
||||
const isInfura = INFURA_PROVIDER_TYPES.includes(type);
|
||||
|
||||
if (isInfura) {
|
||||
this._checkInfuraAvailability(type);
|
||||
} else {
|
||||
this.messenger.publish(NetworkControllerEventTypes.InfuraIsUnblocked);
|
||||
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
|
||||
}
|
||||
|
||||
let networkVersion;
|
||||
let networkVersionError;
|
||||
try {
|
||||
networkVersion = await this._getNetworkId();
|
||||
} catch (error) {
|
||||
networkVersionError = error;
|
||||
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;
|
||||
}
|
||||
if (initialNetwork !== this.networkStore.getState()) {
|
||||
} 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,
|
||||
);
|
||||
|
||||
if (networkVersionError) {
|
||||
this._setNetworkState('loading');
|
||||
// keep network details in sync with network state
|
||||
this._clearNetworkDetails();
|
||||
this.networkStatusStore.putState(networkStatus);
|
||||
|
||||
if (networkStatus === NetworkStatus.Available) {
|
||||
this.networkIdStore.putState(networkId);
|
||||
this.networkDetails.updateState({
|
||||
EIPS: {
|
||||
...this.networkDetails.getState().EIPS,
|
||||
1559: supportsEIP1559,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this._setNetworkState(networkVersion);
|
||||
// look up EIP-1559 support
|
||||
await this.getEIP1559Compatibility();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,13 +409,38 @@ export default class NetworkController extends EventEmitter {
|
||||
// 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 ethQuery = new EthQuery(this._provider);
|
||||
const { provider } = this.getProviderAndBlockTracker();
|
||||
const ethQuery = new EthQuery(provider);
|
||||
|
||||
return await new Promise((resolve, reject) => {
|
||||
ethQuery.sendAsync({ method: 'net_version' }, (error, result) => {
|
||||
if (error) {
|
||||
@ -338,49 +453,24 @@ export default class NetworkController extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to return the latest block for the current network
|
||||
*
|
||||
* @returns {object} Block header
|
||||
* Clears the stored network ID.
|
||||
*/
|
||||
_getLatestBlock() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { provider } = this.getProviderAndBlockTracker();
|
||||
const ethQuery = new EthQuery(provider);
|
||||
ethQuery.sendAsync(
|
||||
{ method: 'eth_getBlockByNumber', params: ['latest', false] },
|
||||
(err, block) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
return resolve(block);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_setNetworkState(network) {
|
||||
this.networkStore.putState(network);
|
||||
_resetNetworkId() {
|
||||
this.networkIdStore.putState(buildDefaultNetworkIdState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set EIP support indication in the networkDetails store
|
||||
*
|
||||
* @param {number} EIPNumber - The number of the EIP to mark support for
|
||||
* @param {boolean} isSupported - True if the EIP is supported
|
||||
* Resets network status to the default ("unknown").
|
||||
*/
|
||||
_setNetworkEIPSupport(EIPNumber, isSupported) {
|
||||
this.networkDetails.putState({
|
||||
EIPS: {
|
||||
[EIPNumber]: isSupported,
|
||||
},
|
||||
});
|
||||
_resetNetworkStatus() {
|
||||
this.networkStatusStore.putState(buildDefaultNetworkStatusState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset EIP support to default (no support)
|
||||
* Clears details previously stored for the network.
|
||||
*/
|
||||
_clearNetworkDetails() {
|
||||
this.networkDetails.putState({ ...defaultNetworkDetailsState });
|
||||
_resetNetworkDetails() {
|
||||
this.networkDetails.putState(buildDefaultNetworkDetailsState());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -394,67 +484,26 @@ export default class NetworkController extends EventEmitter {
|
||||
this._switchNetwork(config);
|
||||
}
|
||||
|
||||
async _checkInfuraAvailability(network) {
|
||||
const rpcUrl = `https://${network}.infura.io/v3/${this._infuraProjectId}`;
|
||||
|
||||
let networkChanged = false;
|
||||
const listener = () => {
|
||||
networkChanged = true;
|
||||
this.messenger.unsubscribe(
|
||||
NetworkControllerEventTypes.NetworkDidChange,
|
||||
listener,
|
||||
);
|
||||
};
|
||||
this.messenger.subscribe(
|
||||
NetworkControllerEventTypes.NetworkDidChange,
|
||||
listener,
|
||||
);
|
||||
|
||||
try {
|
||||
const response = await fetchWithTimeout(rpcUrl, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method: 'eth_blockNumber',
|
||||
params: [],
|
||||
id: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
if (networkChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
this.messenger.publish(NetworkControllerEventTypes.InfuraIsUnblocked);
|
||||
} else {
|
||||
const responseMessage = await response.json();
|
||||
if (networkChanged) {
|
||||
return;
|
||||
}
|
||||
if (responseMessage.error === INFURA_BLOCKED_KEY) {
|
||||
this.messenger.publish(NetworkControllerEventTypes.InfuraIsBlocked);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
log.warn(`MetaMask - Infura availability check failed`, err);
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
// Indicate to subscribers that network is about to change
|
||||
this.messenger.publish(NetworkControllerEventTypes.NetworkWillChange);
|
||||
// Set loading state
|
||||
this._setNetworkState('loading');
|
||||
// Reset network details
|
||||
this._clearNetworkDetails();
|
||||
// Configure the provider appropriately
|
||||
this._resetNetworkId();
|
||||
this._resetNetworkStatus();
|
||||
this._resetNetworkDetails();
|
||||
this._configureProvider(opts);
|
||||
// Notify subscribers that network has changed
|
||||
this.messenger.publish(
|
||||
NetworkControllerEventTypes.NetworkDidChange,
|
||||
opts.type,
|
||||
);
|
||||
this.messenger.publish(NetworkControllerEventTypes.NetworkDidChange);
|
||||
this.lookupNetwork();
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@ import {
|
||||
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP,
|
||||
} from '../../../shared/constants/swaps';
|
||||
import { GasEstimateTypes } from '../../../shared/constants/gas';
|
||||
import { CHAIN_IDS } from '../../../shared/constants/network';
|
||||
import { CHAIN_IDS, NetworkStatus } from '../../../shared/constants/network';
|
||||
import {
|
||||
FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME,
|
||||
FALLBACK_SMART_TRANSACTIONS_DEADLINE,
|
||||
@ -136,10 +136,14 @@ export default class SwapsController {
|
||||
this.indexOfNewestCallInFlight = 0;
|
||||
|
||||
this.ethersProvider = new Web3Provider(provider);
|
||||
this._currentNetwork = networkController.store.getState().network;
|
||||
onNetworkDidChange((network) => {
|
||||
if (network !== 'loading' && network !== this._currentNetwork) {
|
||||
this._currentNetwork = network;
|
||||
this._currentNetworkId = networkController.store.getState().networkId;
|
||||
onNetworkDidChange(() => {
|
||||
const { networkId, networkStatus } = networkController.store.getState();
|
||||
if (
|
||||
networkStatus === NetworkStatus.Available &&
|
||||
networkId !== this._currentNetworkId
|
||||
) {
|
||||
this._currentNetworkId = networkId;
|
||||
this.ethersProvider = new Web3Provider(provider);
|
||||
}
|
||||
});
|
||||
|
@ -4,7 +4,11 @@ import sinon from 'sinon';
|
||||
import { BigNumber } from '@ethersproject/bignumber';
|
||||
import { mapValues } from 'lodash';
|
||||
import BigNumberjs from 'bignumber.js';
|
||||
import { CHAIN_IDS, NETWORK_IDS } from '../../../shared/constants/network';
|
||||
import {
|
||||
CHAIN_IDS,
|
||||
NETWORK_IDS,
|
||||
NetworkStatus,
|
||||
} from '../../../shared/constants/network';
|
||||
import { ETH_SWAPS_TOKEN_OBJECT } from '../../../shared/constants/swaps';
|
||||
import { createTestProviderTools } from '../../../test/stub/provider';
|
||||
import { SECOND } from '../../../shared/constants/time';
|
||||
@ -97,11 +101,10 @@ const MOCK_GET_BUFFERED_GAS_LIMIT = async () => ({
|
||||
function getMockNetworkController() {
|
||||
return {
|
||||
store: {
|
||||
getState: () => {
|
||||
return {
|
||||
network: NETWORK_IDS.GOERLI,
|
||||
};
|
||||
},
|
||||
getState: sinon.stub().returns({
|
||||
networkId: NETWORK_IDS.GOERLI,
|
||||
networkStatus: NetworkStatus.Available,
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -219,6 +222,10 @@ describe('SwapsController', function () {
|
||||
const currentEthersInstance = swapsController.ethersProvider;
|
||||
const changeNetwork = onNetworkDidChange.getCall(0).args[0];
|
||||
|
||||
networkController.store.getState.returns({
|
||||
networkId: NETWORK_IDS.MAINNET,
|
||||
networkStatus: NetworkStatus.Available,
|
||||
});
|
||||
changeNetwork(NETWORK_IDS.MAINNET);
|
||||
|
||||
const newEthersInstance = swapsController.ethersProvider;
|
||||
@ -245,6 +252,10 @@ describe('SwapsController', function () {
|
||||
const currentEthersInstance = swapsController.ethersProvider;
|
||||
const changeNetwork = onNetworkDidChange.getCall(0).args[0];
|
||||
|
||||
networkController.store.getState.returns({
|
||||
networkId: null,
|
||||
networkStatus: NetworkStatus.Unknown,
|
||||
});
|
||||
changeNetwork('loading');
|
||||
|
||||
const newEthersInstance = swapsController.ethersProvider;
|
||||
@ -271,6 +282,10 @@ describe('SwapsController', function () {
|
||||
const currentEthersInstance = swapsController.ethersProvider;
|
||||
const changeNetwork = onNetworkDidChange.getCall(0).args[0];
|
||||
|
||||
networkController.store.getState.returns({
|
||||
networkId: NETWORK_IDS.GOERLI,
|
||||
networkStatus: NetworkStatus.Available,
|
||||
});
|
||||
changeNetwork(NETWORK_IDS.GOERLI);
|
||||
|
||||
const newEthersInstance = swapsController.ethersProvider;
|
||||
|
@ -44,6 +44,7 @@ import {
|
||||
HARDFORKS,
|
||||
CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP,
|
||||
NETWORK_TYPES,
|
||||
NetworkStatus,
|
||||
} from '../../../../shared/constants/network';
|
||||
import {
|
||||
determineTransactionAssetType,
|
||||
@ -115,7 +116,8 @@ const METRICS_STATUS_FAILED = 'failed on-chain';
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param {object} opts.initState - initial transaction list default is an empty array
|
||||
* @param {Function} opts.getNetworkState - Get the current network state.
|
||||
* @param {Function} opts.getNetworkId - Get the current network ID.
|
||||
* @param {Function} opts.getNetworkStatus - Get the current network status.
|
||||
* @param {Function} opts.onNetworkStateChange - Subscribe to network state change events.
|
||||
* @param {object} opts.blockTracker - An instance of eth-blocktracker
|
||||
* @param {object} opts.provider - A network provider.
|
||||
@ -129,7 +131,8 @@ const METRICS_STATUS_FAILED = 'failed on-chain';
|
||||
export default class TransactionController extends EventEmitter {
|
||||
constructor(opts) {
|
||||
super();
|
||||
this.getNetworkState = opts.getNetworkState;
|
||||
this.getNetworkId = opts.getNetworkId;
|
||||
this.getNetworkStatus = opts.getNetworkStatus;
|
||||
this._getCurrentChainId = opts.getCurrentChainId;
|
||||
this.getProviderConfig = opts.getProviderConfig;
|
||||
this._getCurrentNetworkEIP1559Compatibility =
|
||||
@ -167,7 +170,8 @@ export default class TransactionController extends EventEmitter {
|
||||
this.txStateManager = new TransactionStateManager({
|
||||
initState: opts.initState,
|
||||
txHistoryLimit: opts.txHistoryLimit,
|
||||
getNetworkState: this.getNetworkState,
|
||||
getNetworkId: this.getNetworkId,
|
||||
getNetworkStatus: this.getNetworkStatus,
|
||||
getCurrentChainId: opts.getCurrentChainId,
|
||||
});
|
||||
|
||||
@ -226,10 +230,13 @@ export default class TransactionController extends EventEmitter {
|
||||
* @returns {number} The numerical chainId.
|
||||
*/
|
||||
getChainId() {
|
||||
const networkState = this.getNetworkState();
|
||||
const networkStatus = this.getNetworkStatus();
|
||||
const chainId = this._getCurrentChainId();
|
||||
const integerChainId = parseInt(chainId, 16);
|
||||
if (networkState === 'loading' || Number.isNaN(integerChainId)) {
|
||||
if (
|
||||
networkStatus !== NetworkStatus.Available ||
|
||||
Number.isNaN(integerChainId)
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
return integerChainId;
|
||||
@ -272,12 +279,13 @@ export default class TransactionController extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
// For 'rpc' we need to use the same basic configuration as mainnet,
|
||||
// since we only support EVM compatible chains, and then override the
|
||||
// For 'rpc' we need to use the same basic configuration as mainnet, since
|
||||
// we only support EVM compatible chains, and then override the
|
||||
// name, chainId and networkId properties. This is done using the
|
||||
// `forCustomChain` static method on the Common class.
|
||||
const chainId = parseInt(this._getCurrentChainId(), 16);
|
||||
const networkId = this.getNetworkState();
|
||||
const networkStatus = this.getNetworkStatus();
|
||||
const networkId = this.getNetworkId();
|
||||
|
||||
const customChainParams = {
|
||||
name,
|
||||
@ -291,7 +299,8 @@ export default class TransactionController extends EventEmitter {
|
||||
// on a custom network that requires valid network id. I have not ran
|
||||
// into this limitation on any network I have attempted, even when
|
||||
// hardcoding networkId to 'loading'.
|
||||
networkId: networkId === 'loading' ? 0 : parseInt(networkId, 10),
|
||||
networkId:
|
||||
networkStatus === NetworkStatus.Available ? parseInt(networkId, 10) : 0,
|
||||
};
|
||||
|
||||
return Common.forCustomChain(
|
||||
|
@ -27,12 +27,14 @@ import {
|
||||
} from '../../../../shared/constants/gas';
|
||||
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
|
||||
import { ORIGIN_METAMASK } from '../../../../shared/constants/app';
|
||||
import { NetworkStatus } from '../../../../shared/constants/network';
|
||||
import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../shared/lib/transactions-controller-utils';
|
||||
import TransactionController from '.';
|
||||
|
||||
const noop = () => true;
|
||||
const currentNetworkId = '5';
|
||||
const currentChainId = '0x5';
|
||||
const currentNetworkStatus = NetworkStatus.Available;
|
||||
const providerConfig = {
|
||||
type: 'goerli',
|
||||
};
|
||||
@ -46,7 +48,8 @@ describe('Transaction Controller', function () {
|
||||
providerResultStub,
|
||||
fromAccount,
|
||||
fragmentExists,
|
||||
networkStore;
|
||||
networkStatusStore,
|
||||
getCurrentChainId;
|
||||
|
||||
beforeEach(function () {
|
||||
fragmentExists = false;
|
||||
@ -59,22 +62,27 @@ describe('Transaction Controller', function () {
|
||||
provider = createTestProviderTools({
|
||||
scaffold: providerResultStub,
|
||||
networkId: currentNetworkId,
|
||||
chainId: currentNetworkId,
|
||||
chainId: parseInt(currentChainId, 16),
|
||||
}).provider;
|
||||
|
||||
networkStore = new ObservableStore(currentNetworkId);
|
||||
networkStatusStore = new ObservableStore(currentNetworkStatus);
|
||||
|
||||
fromAccount = getTestAccounts()[0];
|
||||
const blockTrackerStub = new EventEmitter();
|
||||
blockTrackerStub.getCurrentBlock = noop;
|
||||
blockTrackerStub.getLatestBlock = noop;
|
||||
|
||||
getCurrentChainId = sinon.stub().callsFake(() => currentChainId);
|
||||
|
||||
txController = new TransactionController({
|
||||
provider,
|
||||
getGasPrice() {
|
||||
return '0xee6b2800';
|
||||
},
|
||||
getNetworkState: () => networkStore.getState(),
|
||||
onNetworkStateChange: (listener) => networkStore.subscribe(listener),
|
||||
getNetworkId: () => currentNetworkId,
|
||||
getNetworkStatus: () => networkStatusStore.getState(),
|
||||
onNetworkStateChange: (listener) =>
|
||||
networkStatusStore.subscribe(listener),
|
||||
getCurrentNetworkEIP1559Compatibility: () => Promise.resolve(false),
|
||||
getCurrentAccountEIP1559Compatibility: () => false,
|
||||
txHistoryLimit: 10,
|
||||
@ -85,7 +93,7 @@ describe('Transaction Controller', function () {
|
||||
}),
|
||||
getProviderConfig: () => providerConfig,
|
||||
getPermittedAccounts: () => undefined,
|
||||
getCurrentChainId: () => currentChainId,
|
||||
getCurrentChainId,
|
||||
getParticipateInMetrics: () => false,
|
||||
trackMetaMetricsEvent: () => undefined,
|
||||
createEventFragment: () => undefined,
|
||||
@ -467,8 +475,8 @@ describe('Transaction Controller', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('should fail if netId is loading', async function () {
|
||||
networkStore.putState('loading');
|
||||
it('should fail if the network status is not "available"', async function () {
|
||||
networkStatusStore.putState(NetworkStatus.Unknown);
|
||||
await assert.rejects(
|
||||
() =>
|
||||
txController.addUnapprovedTransaction(undefined, {
|
||||
@ -1079,8 +1087,19 @@ describe('Transaction Controller', function () {
|
||||
});
|
||||
|
||||
describe('#getChainId', function () {
|
||||
it('returns 0 when the chainId is NaN', function () {
|
||||
networkStore.putState('loading');
|
||||
it('returns the chain ID of the network when it is available', function () {
|
||||
networkStatusStore.putState(NetworkStatus.Available);
|
||||
assert.equal(txController.getChainId(), 5);
|
||||
});
|
||||
|
||||
it('returns 0 when the network is not available', function () {
|
||||
networkStatusStore.putState('asdflsfadf');
|
||||
assert.equal(txController.getChainId(), 0);
|
||||
});
|
||||
|
||||
it('returns 0 when the chain ID cannot be parsed as a hex string', function () {
|
||||
networkStatusStore.putState(NetworkStatus.Available);
|
||||
getCurrentChainId.returns('$fdsjfldf');
|
||||
assert.equal(txController.getChainId(), 0);
|
||||
});
|
||||
});
|
||||
|
@ -7,6 +7,7 @@ import { TransactionStatus } from '../../../../shared/constants/transaction';
|
||||
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
|
||||
import { transactionMatchesNetwork } from '../../../../shared/modules/transaction.utils';
|
||||
import { ORIGIN_METAMASK } from '../../../../shared/constants/app';
|
||||
import { NetworkStatus } from '../../../../shared/constants/network';
|
||||
import {
|
||||
generateHistoryEntry,
|
||||
replayHistory,
|
||||
@ -54,13 +55,15 @@ export const ERROR_SUBMITTING =
|
||||
* transactions list keyed by id
|
||||
* @param {number} [opts.txHistoryLimit] - limit for how many finished
|
||||
* transactions can hang around in state
|
||||
* @param {Function} opts.getNetworkState - Get the current network state.
|
||||
* @param {Function} opts.getNetworkId - Get the current network Id.
|
||||
* @param {Function} opts.getNetworkStatus - Get the current network status.
|
||||
*/
|
||||
export default class TransactionStateManager extends EventEmitter {
|
||||
constructor({
|
||||
initState,
|
||||
txHistoryLimit,
|
||||
getNetworkState,
|
||||
getNetworkId,
|
||||
getNetworkStatus,
|
||||
getCurrentChainId,
|
||||
}) {
|
||||
super();
|
||||
@ -70,7 +73,8 @@ export default class TransactionStateManager extends EventEmitter {
|
||||
...initState,
|
||||
});
|
||||
this.txHistoryLimit = txHistoryLimit;
|
||||
this.getNetworkState = getNetworkState;
|
||||
this.getNetworkId = getNetworkId;
|
||||
this.getNetworkStatus = getNetworkStatus;
|
||||
this.getCurrentChainId = getCurrentChainId;
|
||||
}
|
||||
|
||||
@ -86,9 +90,10 @@ export default class TransactionStateManager extends EventEmitter {
|
||||
* @returns {TransactionMeta} the default txMeta object
|
||||
*/
|
||||
generateTxMeta(opts = {}) {
|
||||
const netId = this.getNetworkState();
|
||||
const networkId = this.getNetworkId();
|
||||
const networkStatus = this.getNetworkStatus();
|
||||
const chainId = this.getCurrentChainId();
|
||||
if (netId === 'loading') {
|
||||
if (networkStatus !== NetworkStatus.Available) {
|
||||
throw new Error('MetaMask is having trouble connecting to the network');
|
||||
}
|
||||
|
||||
@ -128,7 +133,7 @@ export default class TransactionStateManager extends EventEmitter {
|
||||
id: createId(),
|
||||
time: new Date().getTime(),
|
||||
status: TransactionStatus.unapproved,
|
||||
metamaskNetworkId: netId,
|
||||
metamaskNetworkId: networkId,
|
||||
originalGasEstimate: opts.txParams?.gas,
|
||||
userEditedGasLimit: false,
|
||||
chainId,
|
||||
@ -149,12 +154,12 @@ export default class TransactionStateManager extends EventEmitter {
|
||||
*/
|
||||
getUnapprovedTxList() {
|
||||
const chainId = this.getCurrentChainId();
|
||||
const network = this.getNetworkState();
|
||||
const networkId = this.getNetworkId();
|
||||
return pickBy(
|
||||
this.store.getState().transactions,
|
||||
(transaction) =>
|
||||
transaction.status === TransactionStatus.unapproved &&
|
||||
transactionMatchesNetwork(transaction, chainId, network),
|
||||
transactionMatchesNetwork(transaction, chainId, networkId),
|
||||
);
|
||||
}
|
||||
|
||||
@ -413,7 +418,7 @@ export default class TransactionStateManager extends EventEmitter {
|
||||
limit,
|
||||
} = {}) {
|
||||
const chainId = this.getCurrentChainId();
|
||||
const network = this.getNetworkState();
|
||||
const networkId = this.getNetworkId();
|
||||
// searchCriteria is an object that might have values that aren't predicate
|
||||
// methods. When providing any other value type (string, number, etc), we
|
||||
// consider this shorthand for "check the value at key for strict equality
|
||||
@ -442,7 +447,7 @@ export default class TransactionStateManager extends EventEmitter {
|
||||
// when filterToCurrentNetwork is true.
|
||||
if (
|
||||
filterToCurrentNetwork &&
|
||||
transactionMatchesNetwork(transaction, chainId, network) === false
|
||||
transactionMatchesNetwork(transaction, chainId, networkId) === false
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -596,8 +601,7 @@ export default class TransactionStateManager extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all transactions for the given address on the current network,
|
||||
* preferring chainId for comparison over networkId.
|
||||
* Removes all transactions for the given address on the current network.
|
||||
*
|
||||
* @param {string} address - hex string of the from address on the txParams
|
||||
* to remove
|
||||
@ -605,8 +609,8 @@ export default class TransactionStateManager extends EventEmitter {
|
||||
wipeTransactions(address) {
|
||||
// network only tx
|
||||
const { transactions } = this.store.getState();
|
||||
const network = this.getNetworkState();
|
||||
const chainId = this.getCurrentChainId();
|
||||
const networkId = this.getNetworkId();
|
||||
|
||||
// Update state
|
||||
this.store.updateState({
|
||||
@ -614,7 +618,7 @@ export default class TransactionStateManager extends EventEmitter {
|
||||
transactions,
|
||||
(transaction) =>
|
||||
transaction.txParams.from === address &&
|
||||
transactionMatchesNetwork(transaction, chainId, network),
|
||||
transactionMatchesNetwork(transaction, chainId, networkId),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
@ -4,7 +4,11 @@ import {
|
||||
TransactionStatus,
|
||||
TransactionType,
|
||||
} from '../../../../shared/constants/transaction';
|
||||
import { CHAIN_IDS, NETWORK_IDS } from '../../../../shared/constants/network';
|
||||
import {
|
||||
CHAIN_IDS,
|
||||
NETWORK_IDS,
|
||||
NetworkStatus,
|
||||
} from '../../../../shared/constants/network';
|
||||
import { GAS_LIMITS } from '../../../../shared/constants/gas';
|
||||
import { ORIGIN_METAMASK } from '../../../../shared/constants/app';
|
||||
import TxStateManager, { ERROR_SUBMITTING } from './tx-state-manager';
|
||||
@ -45,6 +49,7 @@ function generateTransactions(
|
||||
describe('TransactionStateManager', function () {
|
||||
let txStateManager;
|
||||
const currentNetworkId = NETWORK_IDS.GOERLI;
|
||||
const currentNetworkStatus = NetworkStatus.Available;
|
||||
const currentChainId = CHAIN_IDS.MAINNET;
|
||||
const otherNetworkId = '2';
|
||||
|
||||
@ -54,7 +59,8 @@ describe('TransactionStateManager', function () {
|
||||
transactions: {},
|
||||
},
|
||||
txHistoryLimit: 10,
|
||||
getNetworkState: () => currentNetworkId,
|
||||
getNetworkId: () => currentNetworkId,
|
||||
getNetworkStatus: () => currentNetworkStatus,
|
||||
getCurrentChainId: () => currentChainId,
|
||||
});
|
||||
});
|
||||
@ -181,7 +187,8 @@ describe('TransactionStateManager', function () {
|
||||
[confirmedTx.id]: confirmedTx,
|
||||
},
|
||||
},
|
||||
getNetworkState: () => currentNetworkId,
|
||||
getNetworkId: () => currentNetworkId,
|
||||
getNetworkStatus: () => currentNetworkStatus,
|
||||
getCurrentChainId: () => currentChainId,
|
||||
});
|
||||
|
||||
@ -246,7 +253,8 @@ describe('TransactionStateManager', function () {
|
||||
[confirmedTx3.id]: confirmedTx3,
|
||||
},
|
||||
},
|
||||
getNetworkState: () => currentNetworkId,
|
||||
getNetworkId: () => currentNetworkId,
|
||||
getNetworkStatus: () => currentNetworkStatus,
|
||||
getCurrentChainId: () => currentChainId,
|
||||
});
|
||||
|
||||
@ -355,7 +363,8 @@ describe('TransactionStateManager', function () {
|
||||
[failedTx3Dupe.id]: failedTx3Dupe,
|
||||
},
|
||||
},
|
||||
getNetworkState: () => currentNetworkId,
|
||||
getNetworkId: () => currentNetworkId,
|
||||
getNetworkStatus: () => currentNetworkStatus,
|
||||
getCurrentChainId: () => currentChainId,
|
||||
});
|
||||
|
||||
|
@ -52,7 +52,8 @@ export const SENTRY_STATE = {
|
||||
isUnlocked: true,
|
||||
metaMetricsId: true,
|
||||
nativeCurrency: true,
|
||||
network: true,
|
||||
networkId: true,
|
||||
networkStatus: true,
|
||||
nextNonce: true,
|
||||
participateInMetaMetrics: true,
|
||||
preferences: true,
|
||||
|
@ -74,7 +74,11 @@ import {
|
||||
GAS_DEV_API_BASE_URL,
|
||||
SWAPS_CLIENT_ID,
|
||||
} from '../../shared/constants/swaps';
|
||||
import { CHAIN_IDS, NETWORK_TYPES } from '../../shared/constants/network';
|
||||
import {
|
||||
CHAIN_IDS,
|
||||
NETWORK_TYPES,
|
||||
NetworkStatus,
|
||||
} from '../../shared/constants/network';
|
||||
import { HardwareDeviceNames } from '../../shared/constants/hardware-wallets';
|
||||
import { KeyringType } from '../../shared/constants/keyring';
|
||||
import {
|
||||
@ -334,7 +338,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
{
|
||||
onPreferencesStateChange: (listener) =>
|
||||
this.preferencesController.store.subscribe(listener),
|
||||
onNetworkStateChange: (cb) =>
|
||||
onNetworkStateChange: (cb) => {
|
||||
this.networkController.store.subscribe((networkState) => {
|
||||
const modifiedNetworkState = {
|
||||
...networkState,
|
||||
@ -344,7 +348,8 @@ export default class MetamaskController extends EventEmitter {
|
||||
},
|
||||
};
|
||||
return cb(modifiedNetworkState);
|
||||
}),
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
provider: this.provider,
|
||||
@ -478,6 +483,8 @@ export default class MetamaskController extends EventEmitter {
|
||||
clientId: SWAPS_CLIENT_ID,
|
||||
getProvider: () =>
|
||||
this.networkController.getProviderAndBlockTracker().provider,
|
||||
// NOTE: This option is inaccurately named; it should be called
|
||||
// onNetworkDidChange
|
||||
onNetworkStateChange: networkControllerMessenger.subscribe.bind(
|
||||
networkControllerMessenger,
|
||||
NetworkControllerEventTypes.NetworkDidChange,
|
||||
@ -944,9 +951,11 @@ export default class MetamaskController extends EventEmitter {
|
||||
),
|
||||
getCurrentAccountEIP1559Compatibility:
|
||||
this.getCurrentAccountEIP1559Compatibility.bind(this),
|
||||
getNetworkState: () => this.networkController.store.getState().network,
|
||||
getNetworkId: () => this.networkController.store.getState().networkId,
|
||||
getNetworkStatus: () =>
|
||||
this.networkController.store.getState().networkStatus,
|
||||
onNetworkStateChange: (listener) =>
|
||||
this.networkController.networkStore.subscribe(listener),
|
||||
this.networkController.networkIdStore.subscribe(listener),
|
||||
getCurrentChainId: () =>
|
||||
this.networkController.store.getState().provider.chainId,
|
||||
preferencesStore: this.preferencesController.store,
|
||||
@ -1144,7 +1153,8 @@ export default class MetamaskController extends EventEmitter {
|
||||
return cb(modifiedNetworkState);
|
||||
});
|
||||
},
|
||||
getNetwork: () => this.networkController.store.getState().network,
|
||||
getNetwork: () =>
|
||||
this.networkController.store.getState().networkId ?? 'loading',
|
||||
getNonceLock: this.txController.nonceTracker.getNonceLock.bind(
|
||||
this.txController.nonceTracker,
|
||||
),
|
||||
@ -1666,16 +1676,16 @@ export default class MetamaskController extends EventEmitter {
|
||||
|
||||
function updatePublicConfigStore(memState) {
|
||||
const { chainId } = networkController.store.getState().provider;
|
||||
if (memState.network !== 'loading') {
|
||||
if (memState.networkStatus === NetworkStatus.Available) {
|
||||
publicConfigStore.putState(selectPublicState(chainId, memState));
|
||||
}
|
||||
}
|
||||
|
||||
function selectPublicState(chainId, { isUnlocked, network }) {
|
||||
function selectPublicState(chainId, { isUnlocked, networkId }) {
|
||||
return {
|
||||
isUnlocked,
|
||||
chainId,
|
||||
networkVersion: network,
|
||||
networkVersion: networkId ?? 'loading',
|
||||
};
|
||||
}
|
||||
|
||||
@ -1686,12 +1696,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
* Gets relevant state for the provider of an external origin.
|
||||
*
|
||||
* @param {string} origin - The origin to get the provider state for.
|
||||
* @returns {Promise<{
|
||||
* isUnlocked: boolean,
|
||||
* networkVersion: string,
|
||||
* chainId: string,
|
||||
* accounts: string[],
|
||||
* }>} An object with relevant state properties.
|
||||
* @returns {Promise<{ isUnlocked: boolean, networkVersion: string, chainId: string, accounts: string[] }>} An object with relevant state properties.
|
||||
*/
|
||||
async getProviderState(origin) {
|
||||
return {
|
||||
@ -1709,10 +1714,10 @@ export default class MetamaskController extends EventEmitter {
|
||||
* @returns {object} An object with relevant network state properties.
|
||||
*/
|
||||
getProviderNetworkState(memState) {
|
||||
const { network } = memState || this.getState();
|
||||
const { networkId } = memState || this.getState();
|
||||
return {
|
||||
chainId: this.networkController.store.getState().provider.chainId,
|
||||
networkVersion: network,
|
||||
networkVersion: networkId ?? 'loading',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -779,13 +779,13 @@ describe('MetaMaskController', function () {
|
||||
metamaskController.preferencesController,
|
||||
'getSelectedAddress',
|
||||
);
|
||||
const getNetworkstub = sinon.stub(
|
||||
const getNetworkIdStub = sinon.stub(
|
||||
metamaskController.txController.txStateManager,
|
||||
'getNetworkState',
|
||||
'getNetworkId',
|
||||
);
|
||||
|
||||
selectedAddressStub.returns('0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc');
|
||||
getNetworkstub.returns(42);
|
||||
getNetworkIdStub.returns(42);
|
||||
|
||||
metamaskController.txController.txStateManager._addTransactionsToState([
|
||||
createTxMeta({
|
||||
|
103
app/scripts/migrations/083.test.js
Normal file
103
app/scripts/migrations/083.test.js
Normal file
@ -0,0 +1,103 @@
|
||||
import { migrate } from './083';
|
||||
|
||||
describe('migration #83', () => {
|
||||
it('updates the version metadata', async () => {
|
||||
const originalVersionedData = buildOriginalVersionedData({
|
||||
meta: {
|
||||
version: 9999999,
|
||||
},
|
||||
});
|
||||
|
||||
const newVersionedData = await migrate(originalVersionedData);
|
||||
|
||||
expect(newVersionedData.meta).toStrictEqual({
|
||||
version: 83,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not change the state if the network controller state does not exist', async () => {
|
||||
const originalVersionedData = buildOriginalVersionedData({
|
||||
data: {
|
||||
test: '123',
|
||||
},
|
||||
});
|
||||
|
||||
const newVersionedData = await migrate(originalVersionedData);
|
||||
|
||||
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
|
||||
});
|
||||
|
||||
const nonObjects = [undefined, null, 'test', 1, ['test']];
|
||||
for (const invalidState of nonObjects) {
|
||||
it(`does not change the state if the network controller state is ${invalidState}`, async () => {
|
||||
const originalVersionedData = buildOriginalVersionedData({
|
||||
data: {
|
||||
NetworkController: invalidState,
|
||||
},
|
||||
});
|
||||
|
||||
const newVersionedData = await migrate(originalVersionedData);
|
||||
|
||||
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
|
||||
});
|
||||
}
|
||||
|
||||
it('does not change the state if the network controller state does not include "network"', async () => {
|
||||
const originalVersionedData = buildOriginalVersionedData({
|
||||
data: {
|
||||
NetworkController: {
|
||||
test: '123',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const newVersionedData = await migrate(originalVersionedData);
|
||||
|
||||
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
|
||||
});
|
||||
|
||||
it('replaces "network" in the network controller state with "networkId": null, "networkStatus": "unknown" if it is "loading"', async () => {
|
||||
const originalVersionedData = buildOriginalVersionedData({
|
||||
data: {
|
||||
NetworkController: {
|
||||
network: 'loading',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const newVersionedData = await migrate(originalVersionedData);
|
||||
|
||||
expect(newVersionedData.data).toStrictEqual({
|
||||
NetworkController: {
|
||||
networkId: null,
|
||||
networkStatus: 'unknown',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('replaces "network" in the network controller state with "networkId": network, "networkStatus": "available" if it is not "loading"', async () => {
|
||||
const originalVersionedData = buildOriginalVersionedData({
|
||||
data: {
|
||||
NetworkController: {
|
||||
network: '12345',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const newVersionedData = await migrate(originalVersionedData);
|
||||
|
||||
expect(newVersionedData.data).toStrictEqual({
|
||||
NetworkController: {
|
||||
networkId: '12345',
|
||||
networkStatus: 'available',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function buildOriginalVersionedData({ meta = {}, data = {} } = {}) {
|
||||
return {
|
||||
meta: { version: 999999, ...meta },
|
||||
data: { ...data },
|
||||
};
|
||||
}
|
47
app/scripts/migrations/083.ts
Normal file
47
app/scripts/migrations/083.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { hasProperty, isObject } from '@metamask/utils';
|
||||
|
||||
export const version = 83;
|
||||
|
||||
/**
|
||||
* The `network` property in state was replaced with `networkId` and `networkStatus`.
|
||||
*
|
||||
* @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 (
|
||||
!hasProperty(state, 'NetworkController') ||
|
||||
!isObject(state.NetworkController) ||
|
||||
!hasProperty(state.NetworkController, 'network')
|
||||
) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const NetworkController = { ...state.NetworkController };
|
||||
|
||||
if (NetworkController.network === 'loading') {
|
||||
NetworkController.networkId = null;
|
||||
NetworkController.networkStatus = 'unknown';
|
||||
} else {
|
||||
NetworkController.networkId = NetworkController.network;
|
||||
NetworkController.networkStatus = 'available';
|
||||
}
|
||||
|
||||
delete NetworkController.network;
|
||||
|
||||
return { ...state, NetworkController };
|
||||
}
|
@ -86,6 +86,7 @@ import m079 from './079';
|
||||
import m080 from './080';
|
||||
import * as m081 from './081';
|
||||
import * as m082 from './082';
|
||||
import * as m083 from './083';
|
||||
|
||||
const migrations = [
|
||||
m002,
|
||||
@ -169,6 +170,7 @@ const migrations = [
|
||||
m080,
|
||||
m081,
|
||||
m082,
|
||||
m083,
|
||||
];
|
||||
|
||||
export default migrations;
|
||||
|
@ -689,3 +689,30 @@ export const FEATURED_RPCS: RPCDefinition[] = [
|
||||
|
||||
export const SHOULD_SHOW_LINEA_TESTNET_NETWORK =
|
||||
new Date().getTime() > Date.UTC(2023, 2, 28, 8);
|
||||
|
||||
/**
|
||||
* Represents the availability state of the currently selected network.
|
||||
*/
|
||||
export enum NetworkStatus {
|
||||
/**
|
||||
* The network may or may not be able to receive requests, but either no
|
||||
* attempt has been made to determine this, or an attempt was made but was
|
||||
* unsuccessful.
|
||||
*/
|
||||
Unknown = 'unknown',
|
||||
/**
|
||||
* The network is able to receive and respond to requests.
|
||||
*/
|
||||
Available = 'available',
|
||||
/**
|
||||
* The network is unable to receive and respond to requests for unknown
|
||||
* reasons.
|
||||
*/
|
||||
Unavailable = 'unavailable',
|
||||
/**
|
||||
* The network is not only unavailable, but is also inaccessible for the user
|
||||
* specifically based on their location. This state only applies to Infura
|
||||
* networks.
|
||||
*/
|
||||
Blocked = 'blocked',
|
||||
}
|
||||
|
@ -72,7 +72,8 @@
|
||||
"featureFlags": {
|
||||
"showIncomingTransactions": true
|
||||
},
|
||||
"network": "5",
|
||||
"networkId": "5",
|
||||
"networkStatus": "available",
|
||||
"provider": {
|
||||
"type": "rpc",
|
||||
"chainId": "0x5",
|
||||
|
@ -180,7 +180,8 @@ function defaultFixture() {
|
||||
traits: {},
|
||||
},
|
||||
NetworkController: {
|
||||
network: '1337',
|
||||
networkId: '1337',
|
||||
networkStatus: 'available',
|
||||
provider: {
|
||||
chainId: CHAIN_IDS.LOCALHOST,
|
||||
nickname: 'Localhost 8545',
|
||||
@ -310,7 +311,8 @@ function onboardingFixture() {
|
||||
},
|
||||
},
|
||||
NetworkController: {
|
||||
network: '1337',
|
||||
networkId: '1337',
|
||||
networkStatus: 'available',
|
||||
provider: {
|
||||
ticker: 'ETH',
|
||||
type: 'rpc',
|
||||
@ -487,15 +489,6 @@ class FixtureBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
withNetworkControllerSupportEIP1559() {
|
||||
merge(this.fixture.data.NetworkController, {
|
||||
networkDetails: {
|
||||
EIPS: { 1559: true },
|
||||
},
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
withNftController(data) {
|
||||
merge(
|
||||
this.fixture.data.NftController
|
||||
|
@ -25,6 +25,34 @@ describe('ENS', function () {
|
||||
};
|
||||
});
|
||||
|
||||
await mockServer
|
||||
.forPost(infuraUrl)
|
||||
.withJsonBodyIncluding({ method: 'eth_getBalance' })
|
||||
.thenCallback(() => {
|
||||
return {
|
||||
statusCode: 200,
|
||||
json: {
|
||||
jsonrpc: '2.0',
|
||||
id: '1111111111111111',
|
||||
result: '0x1',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
await mockServer
|
||||
.forPost(infuraUrl)
|
||||
.withJsonBodyIncluding({ method: 'eth_getBlockByNumber' })
|
||||
.thenCallback(() => {
|
||||
return {
|
||||
statusCode: 200,
|
||||
json: {
|
||||
jsonrpc: '2.0',
|
||||
id: '1111111111111111',
|
||||
result: {},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
await mockServer
|
||||
.forPost(infuraUrl)
|
||||
.withJsonBodyIncluding({ method: 'eth_call' })
|
||||
|
@ -298,9 +298,11 @@ describe('Send ETH from dapp using advanced gas controls', function () {
|
||||
dapp: true,
|
||||
fixtures: new FixtureBuilder()
|
||||
.withPermissionControllerConnectedToTestDapp()
|
||||
.withNetworkControllerSupportEIP1559()
|
||||
.build(),
|
||||
ganacheOptions,
|
||||
ganacheOptions: {
|
||||
...ganacheOptions,
|
||||
hardfork: 'london',
|
||||
},
|
||||
title: this.test.title,
|
||||
},
|
||||
async ({ driver }) => {
|
||||
|
@ -11,10 +11,8 @@ jest.mock('../../../../../app/scripts/lib/util', () => ({
|
||||
|
||||
describe('Confirm Detail Row Component', () => {
|
||||
const mockState = {
|
||||
appState: {
|
||||
isLoading: false,
|
||||
},
|
||||
metamask: {
|
||||
networkStatus: 'available',
|
||||
provider: {
|
||||
type: 'rpc',
|
||||
chainId: '0x5',
|
||||
|
@ -21,7 +21,8 @@ describe('Network Dropdown', () => {
|
||||
describe('NetworkDropdown in appState in false', () => {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
network: '1',
|
||||
networkId: '1',
|
||||
networkStatus: 'available',
|
||||
provider: {
|
||||
type: 'test',
|
||||
},
|
||||
@ -55,7 +56,8 @@ describe('Network Dropdown', () => {
|
||||
describe('NetworkDropdown in appState is true and show test networks is true', () => {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
network: '1',
|
||||
networkId: '1',
|
||||
networkStatus: 'available',
|
||||
provider: {
|
||||
type: 'test',
|
||||
},
|
||||
@ -133,7 +135,8 @@ describe('Network Dropdown', () => {
|
||||
describe('NetworkDropdown in appState is true and show test networks is false', () => {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
network: '1',
|
||||
networkId: '1',
|
||||
networkStatus: 'available',
|
||||
provider: {
|
||||
type: 'test',
|
||||
},
|
||||
|
@ -18,7 +18,7 @@ const customTransaction = ({
|
||||
userFeeLevel: estimateUsed ? 'low' : 'medium',
|
||||
blockNumber: `${10902987 + i}`,
|
||||
id: 4678200543090545 + i,
|
||||
metamaskNetworkId: testData?.metamask?.network,
|
||||
metamaskNetworkId: testData?.metamask?.networkId,
|
||||
chainId: testData?.metamask?.provider?.chainId,
|
||||
status: 'confirmed',
|
||||
time: 1600654021000,
|
||||
|
@ -343,7 +343,8 @@ describe('Confirm Transaction Duck', () => {
|
||||
metamask: {
|
||||
conversionRate: 468.58,
|
||||
currentCurrency: 'usd',
|
||||
network: '5',
|
||||
networkId: '5',
|
||||
networkStatus: 'available',
|
||||
provider: {
|
||||
chainId: '0x5',
|
||||
},
|
||||
@ -368,7 +369,6 @@ describe('Confirm Transaction Duck', () => {
|
||||
},
|
||||
confirmTransaction: {},
|
||||
};
|
||||
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureMockStore(middlewares);
|
||||
const store = mockStore(mockState);
|
||||
|
@ -41,7 +41,8 @@ describe('MetaMask Reducers', () => {
|
||||
conversionRate: 1200.88200327,
|
||||
nativeCurrency: 'ETH',
|
||||
useCurrencyRateCheck: true,
|
||||
network: '5',
|
||||
networkId: '5',
|
||||
networkStatus: 'available',
|
||||
provider: {
|
||||
type: 'testnet',
|
||||
chainId: '0x5',
|
||||
|
@ -9,7 +9,7 @@ export default function txHelper(
|
||||
decryptMsgs: Record<string, any> | null,
|
||||
encryptionPublicKeyMsgs: Record<string, any> | null,
|
||||
typedMessages: Record<string, any> | null,
|
||||
network?: string,
|
||||
networkId?: string | null,
|
||||
chainId?: string,
|
||||
): Record<string, any> {
|
||||
log.debug('tx-helper called with params:');
|
||||
@ -20,13 +20,13 @@ export default function txHelper(
|
||||
decryptMsgs,
|
||||
encryptionPublicKeyMsgs,
|
||||
typedMessages,
|
||||
network,
|
||||
networkId,
|
||||
chainId,
|
||||
});
|
||||
|
||||
const txValues = network
|
||||
const txValues = networkId
|
||||
? valuesFor(unapprovedTxs).filter((txMeta) =>
|
||||
transactionMatchesNetwork(txMeta, chainId, network),
|
||||
transactionMatchesNetwork(txMeta, chainId, networkId),
|
||||
)
|
||||
: valuesFor(unapprovedTxs);
|
||||
log.debug(`tx helper found ${txValues.length} unapproved txs`);
|
||||
|
@ -160,7 +160,7 @@ async function startApp(metamaskState, backgroundConnection, opts) {
|
||||
metamaskState.unapprovedDecryptMsgs,
|
||||
metamaskState.unapprovedEncryptionPublicKeyMsgs,
|
||||
metamaskState.unapprovedTypedMessages,
|
||||
metamaskState.network,
|
||||
metamaskState.networkId,
|
||||
metamaskState.provider.chainId,
|
||||
);
|
||||
const numberOfUnapprovedTx = unapprovedTxsAll.length;
|
||||
|
@ -63,7 +63,7 @@ const ConfirmTxScreen = ({ match }) => {
|
||||
unapprovedMsgs,
|
||||
unapprovedPersonalMsgs,
|
||||
unapprovedTypedMessages,
|
||||
network,
|
||||
networkId,
|
||||
blockGasLimit,
|
||||
provider: { chainId },
|
||||
} = useSelector((state) => state.metamask);
|
||||
@ -76,7 +76,7 @@ const ConfirmTxScreen = ({ match }) => {
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
network,
|
||||
networkId,
|
||||
chainId,
|
||||
);
|
||||
if (unconfTxList.length === 0 && !sendTo && unapprovedMessagesTotal === 0) {
|
||||
@ -101,7 +101,7 @@ const ConfirmTxScreen = ({ match }) => {
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
network,
|
||||
networkId,
|
||||
chainId,
|
||||
);
|
||||
const prevTxData = prevUnconfTxList[prevIndex] || {};
|
||||
@ -114,7 +114,7 @@ const ConfirmTxScreen = ({ match }) => {
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
network,
|
||||
networkId,
|
||||
chainId,
|
||||
);
|
||||
|
||||
@ -137,7 +137,7 @@ const ConfirmTxScreen = ({ match }) => {
|
||||
chainId,
|
||||
currentNetworkTxList,
|
||||
match,
|
||||
network,
|
||||
networkId,
|
||||
sendTo,
|
||||
unapprovedMessagesTotal,
|
||||
unapprovedTxs,
|
||||
@ -151,7 +151,7 @@ const ConfirmTxScreen = ({ match }) => {
|
||||
unapprovedMsgs,
|
||||
unapprovedPersonalMsgs,
|
||||
unapprovedTypedMessages,
|
||||
network,
|
||||
networkId,
|
||||
chainId,
|
||||
);
|
||||
|
||||
|
@ -101,7 +101,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
conversionRate,
|
||||
identities,
|
||||
addressBook,
|
||||
network,
|
||||
networkId,
|
||||
unapprovedTxs,
|
||||
nextNonce,
|
||||
provider: { chainId },
|
||||
@ -160,7 +160,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
|
||||
const currentNetworkUnapprovedTxs = Object.keys(unapprovedTxs)
|
||||
.filter((key) =>
|
||||
transactionMatchesNetwork(unapprovedTxs[key], chainId, network),
|
||||
transactionMatchesNetwork(unapprovedTxs[key], chainId, networkId),
|
||||
)
|
||||
.reduce((acc, key) => ({ ...acc, [key]: unapprovedTxs[key] }), {});
|
||||
const unapprovedTxCount = valuesFor(currentNetworkUnapprovedTxs).length;
|
||||
|
@ -33,6 +33,7 @@ const baseStore = {
|
||||
unapprovedTxs: {
|
||||
1: {
|
||||
id: 1,
|
||||
metamaskNetworkId: '5',
|
||||
txParams: {
|
||||
from: '0x0',
|
||||
to: '0x85c1685cfceaa5c0bdb1609fc536e9a8387dd65e',
|
||||
@ -58,6 +59,7 @@ const baseStore = {
|
||||
accounts: ['0x0'],
|
||||
},
|
||||
],
|
||||
networkId: '5',
|
||||
networkDetails: {
|
||||
EIPS: {},
|
||||
},
|
||||
|
@ -31,7 +31,7 @@ describe('Confirm Transaction Selector', () => {
|
||||
unapprovedMsgCount: 1,
|
||||
unapprovedPersonalMsgCount: 1,
|
||||
unapprovedTypedMessagesCount: 1,
|
||||
network: '5',
|
||||
networkId: '5',
|
||||
provider: {
|
||||
chainId: '0x5',
|
||||
},
|
||||
|
@ -24,6 +24,7 @@ import {
|
||||
CHAIN_ID_TO_RPC_URL_MAP,
|
||||
CHAIN_IDS,
|
||||
NETWORK_TYPES,
|
||||
NetworkStatus,
|
||||
} from '../../shared/constants/network';
|
||||
import {
|
||||
WebHIDConnectedStatuses,
|
||||
@ -77,16 +78,13 @@ import { getPermissionSubjects } from './permissions';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
/**
|
||||
* One of the only remaining valid uses of selecting the network subkey of the
|
||||
* metamask state tree is to determine if the network is currently 'loading'.
|
||||
* Returns true if the currently selected network is inaccessible or whether no
|
||||
* provider has been set yet for the currently selected network.
|
||||
*
|
||||
* This will be used for all cases where this state key is accessed only for that
|
||||
* purpose.
|
||||
*
|
||||
* @param {object} state - redux state object
|
||||
* @param {object} state - Redux state object.
|
||||
*/
|
||||
export function isNetworkLoading(state) {
|
||||
return state.metamask.network === 'loading';
|
||||
return state.metamask.networkStatus !== NetworkStatus.Available;
|
||||
}
|
||||
|
||||
export function getNetworkIdentifier(state) {
|
||||
@ -248,7 +246,7 @@ export function getAccountType(state) {
|
||||
* @param {object} state - redux state object
|
||||
*/
|
||||
export function deprecatedGetCurrentNetworkId(state) {
|
||||
return state.metamask.network;
|
||||
return state.metamask.networkId ?? 'loading';
|
||||
}
|
||||
|
||||
export const getMetaMaskAccounts = createSelector(
|
||||
|
@ -29,14 +29,14 @@ export const incomingTxListSelector = (state) => {
|
||||
}
|
||||
|
||||
const {
|
||||
network,
|
||||
networkId,
|
||||
provider: { chainId },
|
||||
} = state.metamask;
|
||||
const selectedAddress = getSelectedAddress(state);
|
||||
return Object.values(state.metamask.incomingTransactions).filter(
|
||||
(tx) =>
|
||||
tx.txParams.to === selectedAddress &&
|
||||
transactionMatchesNetwork(tx, chainId, network),
|
||||
transactionMatchesNetwork(tx, chainId, networkId),
|
||||
);
|
||||
};
|
||||
export const unapprovedMsgsSelector = (state) => state.metamask.unapprovedMsgs;
|
||||
|
@ -6,6 +6,7 @@ import { GasEstimateType, GasFeeEstimates } from '@metamask/gas-fee-controller';
|
||||
import rootReducer from '../ducks';
|
||||
import { LedgerTransportTypes } from '../../shared/constants/hardware-wallets';
|
||||
import { TransactionMeta } from '../../shared/constants/transaction';
|
||||
import type { NetworkStatus } from '../../shared/constants/network';
|
||||
|
||||
/**
|
||||
* This interface is temporary and is copied from the message-manager.js file
|
||||
@ -65,7 +66,8 @@ interface TemporaryBackgroundState {
|
||||
unapprovedMsgs: MessagesIndexedById;
|
||||
unapprovedPersonalMsgs: MessagesIndexedById;
|
||||
unapprovedTypedMessages: MessagesIndexedById;
|
||||
network: string;
|
||||
networkId: string | null;
|
||||
networkStatus: NetworkStatus;
|
||||
pendingApprovals: ApprovalControllerState['pendingApprovals'];
|
||||
knownMethodData?: {
|
||||
[fourBytePrefix: string]: Record<string, unknown>;
|
||||
|
Loading…
Reference in New Issue
Block a user