diff --git a/app/scripts/migrations/135.test.ts b/app/scripts/migrations/135.test.ts new file mode 100644 index 000000000000..137f55ef2b5c --- /dev/null +++ b/app/scripts/migrations/135.test.ts @@ -0,0 +1,131 @@ +import { cloneDeep } from 'lodash'; +import { NetworkState } from '@metamask/network-controller'; +import { infuraProjectId } from '../../../shared/constants/network'; +import { migrate, version } from './135'; + +const oldVersion = 134; +const BASE_CHAIN_ID = '0x2105'; + +describe('migration #135', () => { + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + + const newStorage = await migrate(cloneDeep(oldStorage)); + + expect(newStorage.meta).toStrictEqual({ version }); + }); + + describe('Base Network Migration', () => { + it('does nothing if networkConfigurationsByChainId is not in state', async () => { + const oldState = { + OtherController: {}, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('replaces "https://mainnet.base.org" with "base-mainnet.infura.io" when it exists', async () => { + const oldState = { + NetworkController: { + networkConfigurationsByChainId: { + [BASE_CHAIN_ID]: { + rpcEndpoints: [ + { + url: 'https://mainnet.base.org', + type: 'custom', + networkClientId: 'base-mainnet', + }, + ], + defaultRpcEndpointIndex: 0, + }, + }, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + const updatedNetworkController = transformedState.data + .NetworkController as NetworkState; + + expect( + updatedNetworkController.networkConfigurationsByChainId[BASE_CHAIN_ID] + .rpcEndpoints[0].url, + ).toEqual(`https://base-mainnet.infura.io/v3/${infuraProjectId}`); + }); + + it('does not modify RPC endpoints other than "https://mainnet.base.org"', async () => { + const oldState = { + NetworkController: { + networkConfigurationsByChainId: { + [BASE_CHAIN_ID]: { + rpcEndpoints: [ + { + url: 'https://other.rpc', + type: 'custom', + networkClientId: 'other-mainnet', + }, + ], + defaultRpcEndpointIndex: 0, + }, + }, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + const updatedNetworkController = transformedState.data + .NetworkController as NetworkState; + + expect( + updatedNetworkController.networkConfigurationsByChainId[BASE_CHAIN_ID] + .rpcEndpoints[0].url, + ).toEqual('https://other.rpc'); + }); + + it('keeps defaultRpcEndpointIndex unchanged when replacing "https://mainnet.base.org"', async () => { + const oldState = { + NetworkController: { + networkConfigurationsByChainId: { + [BASE_CHAIN_ID]: { + rpcEndpoints: [ + { + url: 'https://mainnet.base.org', + type: 'custom', + networkClientId: 'base-mainnet', + }, + ], + defaultRpcEndpointIndex: 0, + }, + }, + }, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + const updatedNetworkController = transformedState.data + .NetworkController as NetworkState; + + expect( + updatedNetworkController.networkConfigurationsByChainId[BASE_CHAIN_ID] + .defaultRpcEndpointIndex, + ).toEqual(0); + }); + }); +}); diff --git a/app/scripts/migrations/135.ts b/app/scripts/migrations/135.ts new file mode 100644 index 000000000000..637ac8b7a57f --- /dev/null +++ b/app/scripts/migrations/135.ts @@ -0,0 +1,78 @@ +import { hasProperty, isObject } from '@metamask/utils'; +import { cloneDeep } from 'lodash'; +import { infuraProjectId } from '../../../shared/constants/network'; + +export const version = 135; +const BASE_CHAIN_ID = '0x2105'; + +/** + * Replace all occurrences of "https://mainnet.base.org" with + * "https://base-mainnet.infura.io/v3/${infuraProjectId}" in the Base network configuration. + * + * @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; +}) { + const versionedData = cloneDeep(originalVersionedData); + versionedData.meta.version = version; + versionedData.data = transformState(versionedData.data); + return versionedData; +} + +function transformState(state: Record) { + if ( + hasProperty(state, 'NetworkController') && + isObject(state.NetworkController) && + hasProperty(state.NetworkController, 'networkConfigurationsByChainId') && + isObject(state.NetworkController.networkConfigurationsByChainId) + ) { + const { networkConfigurationsByChainId } = state.NetworkController; + + // Check for Base network configuration (chainId 8453 / 0x2105) + const baseNetworkConfig = networkConfigurationsByChainId[BASE_CHAIN_ID]; + if (isObject(baseNetworkConfig)) { + const { rpcEndpoints } = baseNetworkConfig; + + if (Array.isArray(rpcEndpoints)) { + // Find the first occurrence of "https://mainnet.base.org" + // rpc URL are Unique so we can use findIndex + const index = rpcEndpoints.findIndex( + (endpoint) => + isObject(endpoint) && endpoint.url === 'https://mainnet.base.org', + ); + + if (index !== -1) { + // Replace the URL with the new Infura URL + rpcEndpoints[index] = { + ...rpcEndpoints[index], + url: `https://base-mainnet.infura.io/v3/${infuraProjectId}`, + }; + + // Update the configuration + networkConfigurationsByChainId[BASE_CHAIN_ID] = { + ...baseNetworkConfig, + rpcEndpoints, + }; + + return { + ...state, + NetworkController: { + ...state.NetworkController, + networkConfigurationsByChainId, + }, + }; + } + } + } + } + + return state; +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 887732ad9dbc..459578ba3eda 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -155,6 +155,7 @@ const migrations = [ require('./132'), require('./133'), require('./134'), + require('./135'), ]; export default migrations; diff --git a/shared/constants/network.test.ts b/shared/constants/network.test.ts index 9d9ca72b46cd..1fe489488f8f 100644 --- a/shared/constants/network.test.ts +++ b/shared/constants/network.test.ts @@ -87,11 +87,11 @@ describe('NetworkConstants', () => { expect(zksyncEraRpc.rpcEndpoints[0].url).not.toContain('infura.io'); }); - it('base entry should not use Infura', () => { + it('base entry should use Infura', () => { const [baseRpc] = FEATURED_RPCS.filter( (rpc) => rpc.chainId === CHAIN_IDS.BASE, ); - expect(baseRpc.rpcEndpoints[0].url).not.toContain('infura.io'); + expect(baseRpc.rpcEndpoints[0].url).toContain('infura.io'); }); }); }); diff --git a/shared/constants/network.ts b/shared/constants/network.ts index 41f1d9fd0d95..5901d8811c5d 100644 --- a/shared/constants/network.ts +++ b/shared/constants/network.ts @@ -1067,7 +1067,7 @@ export const FEATURED_RPCS: AddNetworkFields[] = [ nativeCurrency: CURRENCY_SYMBOLS.ETH, rpcEndpoints: [ { - url: `https://mainnet.base.org`, + url: `https://base-mainnet.infura.io/v3/${infuraProjectId}`, type: RpcEndpointType.Custom, }, ],