diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 339cf0b2b..d3c554cc1 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -805,6 +805,9 @@ "connectingToLineaGoerli": { "message": "Connecting to Linea Goerli test network" }, + "connectingToLineaMainnet": { + "message": "Connecting to Linea Mainnet" + }, "connectingToMainnet": { "message": "Connecting to Ethereum Mainnet" }, @@ -2138,6 +2141,9 @@ "lineaGoerli": { "message": "Linea Goerli test network" }, + "lineaMainnet": { + "message": "Linea Mainnet" + }, "link": { "message": "Link" }, diff --git a/app/images/linea-logo-mainnet.png b/app/images/linea-logo-mainnet.png new file mode 100644 index 000000000..23a9f9d1a Binary files /dev/null and b/app/images/linea-logo-mainnet.png differ diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index e2bf8025b..0386d4d94 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -6,6 +6,7 @@ import { setDashboardCookie } from '@metamask-institutional/portfolio-dashboard' import { IPFS_DEFAULT_GATEWAY_URL } from '../../../shared/constants/network'; import { LedgerTransportTypes } from '../../../shared/constants/hardware-wallets'; import { ThemeType } from '../../../shared/constants/preferences'; +import { shouldShowLineaMainnet } from '../../../shared/modules/network.utils'; export default class PreferencesController { /** @@ -69,6 +70,7 @@ export default class PreferencesController { : LedgerTransportTypes.u2f, transactionSecurityCheckEnabled: false, theme: ThemeType.os, + isLineaMainnetReleased: false, ...opts.initState, }; @@ -92,6 +94,8 @@ export default class PreferencesController { global.setPreference = (key, value) => { return this.setFeatureFlag(key, value); }; + + this._showShouldLineaMainnetNetwork(); } // PUBLIC METHODS @@ -574,4 +578,12 @@ export default class PreferencesController { this.store.updateState({ infuraBlocked: isBlocked }); } + + /** + * A method to check is the linea mainnet network should be displayed + */ + _showShouldLineaMainnetNetwork() { + const showLineaMainnet = shouldShowLineaMainnet(); + this.store.updateState({ isLineaMainnetReleased: showLineaMainnet }); + } } diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 93d3ab382..cc8fc02c6 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -280,7 +280,8 @@ export default class TransactionController extends EventEmitter { if ( type !== NETWORK_TYPES.RPC && type !== NETWORK_TYPES.SEPOLIA && - type !== NETWORK_TYPES.LINEA_GOERLI + type !== NETWORK_TYPES.LINEA_GOERLI && + type !== NETWORK_TYPES.LINEA_MAINNET ) { return new Common({ chain: type, diff --git a/development/states/navigate-txs.json b/development/states/navigate-txs.json index 32daeee71..8cd084b85 100644 --- a/development/states/navigate-txs.json +++ b/development/states/navigate-txs.json @@ -305,7 +305,8 @@ "mainnet": "ok", "goerli": "ok", "sepolia": "ok", - "lineaGoerli": "ok" + "lineaGoerli": "ok", + "lineaMainnet": "ok" } }, "send": { diff --git a/shared/constants/network.ts b/shared/constants/network.ts index 7e7d26d7b..b25353dbe 100644 --- a/shared/constants/network.ts +++ b/shared/constants/network.ts @@ -96,6 +96,7 @@ export const NETWORK_TYPES = { RPC: 'rpc', SEPOLIA: 'sepolia', LINEA_GOERLI: 'linea-goerli', + LINEA_MAINNET: 'linea-mainnet', } as const; /** @@ -122,6 +123,7 @@ export const NETWORK_IDS = { LOCALHOST: '1337', SEPOLIA: '11155111', LINEA_GOERLI: '59140', + LINEA_MAINNET: '59144', } as const; /** @@ -148,6 +150,7 @@ export const CHAIN_IDS = { PALM: '0x2a15c308d', SEPOLIA: '0xaa36a7', LINEA_GOERLI: '0xe704', + LINEA_MAINNET: '0xe708', AURORA: '0x4e454152', MOONBEAM: '0x504', MOONBEAM_TESTNET: '0x507', @@ -165,6 +168,7 @@ export const MAINNET_DISPLAY_NAME = 'Ethereum Mainnet'; export const GOERLI_DISPLAY_NAME = 'Goerli'; export const SEPOLIA_DISPLAY_NAME = 'Sepolia'; export const LINEA_GOERLI_DISPLAY_NAME = 'Linea Goerli'; +export const LINEA_MAINNET_DISPLAY_NAME = 'Linea Mainnet'; export const LOCALHOST_DISPLAY_NAME = 'Localhost 8545'; export const BSC_DISPLAY_NAME = 'Binance Smart Chain'; export const POLYGON_DISPLAY_NAME = 'Polygon'; @@ -197,6 +201,9 @@ export const SEPOLIA_RPC_URL = getRpcUrl({ network: NETWORK_TYPES.SEPOLIA }); export const LINEA_GOERLI_RPC_URL = getRpcUrl({ network: NETWORK_TYPES.LINEA_GOERLI, }); +export const LINEA_MAINNET_RPC_URL = getRpcUrl({ + network: NETWORK_TYPES.LINEA_MAINNET, +}); export const LOCALHOST_RPC_URL = 'http://localhost:8545'; /** @@ -230,6 +237,7 @@ export const CURRENCY_SYMBOLS = { export const ETH_TOKEN_IMAGE_URL = './images/eth_logo.png'; export const LINEA_GOERLI_TOKEN_IMAGE_URL = './images/linea-logo-testnet.png'; +export const LINEA_MAINNET_TOKEN_IMAGE_URL = './images/linea-logo-mainnet.png'; export const TEST_ETH_TOKEN_IMAGE_URL = './images/black-eth-logo.svg'; export const BNB_TOKEN_IMAGE_URL = './images/bnb.png'; export const MATIC_TOKEN_IMAGE_URL = './images/matic-token.png'; @@ -247,6 +255,7 @@ export const INFURA_PROVIDER_TYPES = [ NETWORK_TYPES.GOERLI, NETWORK_TYPES.SEPOLIA, NETWORK_TYPES.LINEA_GOERLI, + NETWORK_TYPES.LINEA_MAINNET, ] as const; export const TEST_CHAINS = [ @@ -260,7 +269,10 @@ const typedCapitalize = (k: K): Capitalize => capitalize(k) as Capitalize; export const TEST_NETWORK_TICKER_MAP: { - [K in Exclude]: string; + [K in Exclude< + NetworkType, + 'localhost' | 'mainnet' | 'rpc' | 'linea-mainnet' + >]: string; } = { [NETWORK_TYPES.GOERLI]: `${typedCapitalize(NETWORK_TYPES.GOERLI)}${ CURRENCY_SYMBOLS.ETH @@ -298,6 +310,11 @@ export const BUILT_IN_NETWORKS = { chainId: CHAIN_IDS.MAINNET, blockExplorerUrl: `https://etherscan.io`, }, + [NETWORK_TYPES.LINEA_MAINNET]: { + networkId: NETWORK_IDS.LINEA_MAINNET, + chainId: CHAIN_IDS.LINEA_MAINNET, + blockExplorerUrl: 'https://lineascan.build', + }, [NETWORK_TYPES.LOCALHOST]: { networkId: NETWORK_IDS.LOCALHOST, chainId: CHAIN_IDS.LOCALHOST, @@ -316,18 +333,21 @@ export const NETWORK_TO_NAME_MAP = { [NETWORK_TYPES.GOERLI]: GOERLI_DISPLAY_NAME, [NETWORK_TYPES.SEPOLIA]: SEPOLIA_DISPLAY_NAME, [NETWORK_TYPES.LINEA_GOERLI]: LINEA_GOERLI_DISPLAY_NAME, + [NETWORK_TYPES.LINEA_MAINNET]: LINEA_MAINNET_DISPLAY_NAME, [NETWORK_TYPES.LOCALHOST]: LOCALHOST_DISPLAY_NAME, [NETWORK_IDS.GOERLI]: GOERLI_DISPLAY_NAME, [NETWORK_IDS.SEPOLIA]: SEPOLIA_DISPLAY_NAME, [NETWORK_IDS.LINEA_GOERLI]: LINEA_GOERLI_DISPLAY_NAME, [NETWORK_IDS.MAINNET]: MAINNET_DISPLAY_NAME, + [NETWORK_IDS.LINEA_MAINNET]: LINEA_MAINNET_DISPLAY_NAME, [NETWORK_IDS.LOCALHOST]: LOCALHOST_DISPLAY_NAME, [CHAIN_IDS.GOERLI]: GOERLI_DISPLAY_NAME, [CHAIN_IDS.SEPOLIA]: SEPOLIA_DISPLAY_NAME, [CHAIN_IDS.LINEA_GOERLI]: LINEA_GOERLI_DISPLAY_NAME, [CHAIN_IDS.MAINNET]: MAINNET_DISPLAY_NAME, + [CHAIN_IDS.LINEA_MAINNET]: LINEA_MAINNET_DISPLAY_NAME, [CHAIN_IDS.LOCALHOST]: LOCALHOST_DISPLAY_NAME, } as const; @@ -336,6 +356,7 @@ export const CHAIN_ID_TO_TYPE_MAP = { [CHAIN_IDS.GOERLI]: NETWORK_TYPES.GOERLI, [CHAIN_IDS.SEPOLIA]: NETWORK_TYPES.SEPOLIA, [CHAIN_IDS.LINEA_GOERLI]: NETWORK_TYPES.LINEA_GOERLI, + [CHAIN_IDS.LINEA_MAINNET]: NETWORK_TYPES.LINEA_MAINNET, [CHAIN_IDS.LOCALHOST]: NETWORK_TYPES.LOCALHOST, } as const; @@ -344,12 +365,14 @@ export const CHAIN_ID_TO_RPC_URL_MAP = { [CHAIN_IDS.SEPOLIA]: SEPOLIA_RPC_URL, [CHAIN_IDS.LINEA_GOERLI]: LINEA_GOERLI_RPC_URL, [CHAIN_IDS.MAINNET]: MAINNET_RPC_URL, + [CHAIN_IDS.LINEA_MAINNET]: LINEA_MAINNET_RPC_URL, [CHAIN_IDS.LOCALHOST]: LOCALHOST_RPC_URL, } as const; export const CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP = { [CHAIN_IDS.MAINNET]: ETH_TOKEN_IMAGE_URL, [CHAIN_IDS.LINEA_GOERLI]: LINEA_GOERLI_TOKEN_IMAGE_URL, + [CHAIN_IDS.LINEA_MAINNET]: LINEA_MAINNET_TOKEN_IMAGE_URL, [CHAIN_IDS.AVALANCHE]: AVAX_TOKEN_IMAGE_URL, [CHAIN_IDS.BSC]: BNB_TOKEN_IMAGE_URL, [CHAIN_IDS.POLYGON]: MATIC_TOKEN_IMAGE_URL, @@ -367,6 +390,7 @@ export const NETWORK_ID_TO_ETHERS_NETWORK_NAME_MAP = { [NETWORK_IDS.SEPOLIA]: NETWORK_TYPES.SEPOLIA, [NETWORK_IDS.LINEA_GOERLI]: NETWORK_TYPES.LINEA_GOERLI, [NETWORK_IDS.MAINNET]: NETWORK_NAMES.HOMESTEAD, + [NETWORK_IDS.LINEA_MAINNET]: NETWORK_TYPES.LINEA_MAINNET, } as const; export const CHAIN_ID_TO_NETWORK_ID_MAP = { @@ -374,6 +398,7 @@ export const CHAIN_ID_TO_NETWORK_ID_MAP = { [CHAIN_IDS.GOERLI]: NETWORK_IDS.GOERLI, [CHAIN_IDS.SEPOLIA]: NETWORK_IDS.SEPOLIA, [CHAIN_IDS.LINEA_GOERLI]: NETWORK_IDS.LINEA_GOERLI, + [CHAIN_IDS.LINEA_MAINNET]: NETWORK_IDS.LINEA_MAINNET, [CHAIN_IDS.LOCALHOST]: NETWORK_IDS.LOCALHOST, } as const; @@ -420,6 +445,11 @@ export const ETHERSCAN_SUPPORTED_NETWORKS = { subdomain: 'goerli', networkId: CHAIN_ID_TO_NETWORK_ID_MAP[CHAIN_IDS.LINEA_GOERLI], }, + [CHAIN_IDS.LINEA_MAINNET]: { + domain: 'lineascan.build', + subdomain: defaultEtherscanSubdomainPrefix, + networkId: CHAIN_ID_TO_NETWORK_ID_MAP[CHAIN_IDS.LINEA_MAINNET], + }, [CHAIN_IDS.BSC]: { domain: 'bscscan.com', subdomain: defaultEtherscanSubdomainPrefix, @@ -519,6 +549,7 @@ export const BUYABLE_CHAINS_MAP: { | typeof CHAIN_IDS.FANTOM_TESTNET | typeof CHAIN_IDS.MOONBEAM_TESTNET | typeof CHAIN_IDS.LINEA_GOERLI + | typeof CHAIN_IDS.LINEA_MAINNET | typeof CHAIN_IDS.GOERLI >]: BuyableChainSettings; } = { diff --git a/shared/modules/network.utils.ts b/shared/modules/network.utils.ts index 80871cda7..9aeb439eb 100644 --- a/shared/modules/network.utils.ts +++ b/shared/modules/network.utils.ts @@ -56,3 +56,7 @@ export function isTokenDetectionEnabledForNetwork(chainId: string | undefined) { function isSafeInteger(value: unknown): value is number { return Number.isSafeInteger(value); } + +export function shouldShowLineaMainnet(): boolean { + return new Date().getTime() > Date.UTC(2023, 6, 11, 18); +} diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 428814562..884c9b94e 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -161,6 +161,7 @@ function defaultFixture() { trezorModel: null, usedNetworks: { [CHAIN_IDS.MAINNET]: true, + [CHAIN_IDS.LINEA_MAINNET]: true, [CHAIN_IDS.GOERLI]: true, [CHAIN_IDS.LOCALHOST]: true, }, @@ -187,6 +188,7 @@ function defaultFixture() { incomingTransactions: {}, incomingTxLastFetchedBlockByChainId: { [CHAIN_IDS.MAINNET]: null, + [CHAIN_IDS.LINEA_MAINNET]: null, [CHAIN_IDS.GOERLI]: null, [CHAIN_IDS.SEPOLIA]: null, [CHAIN_IDS.LINEA_GOERLI]: null, @@ -329,6 +331,7 @@ function onboardingFixture() { trezorModel: null, usedNetworks: { [CHAIN_IDS.MAINNET]: true, + [CHAIN_IDS.LINEA_MAINNET]: true, [CHAIN_IDS.GOERLI]: true, [CHAIN_IDS.LOCALHOST]: true, }, diff --git a/ui/components/app/loading-network-screen/loading-network-screen.component.js b/ui/components/app/loading-network-screen/loading-network-screen.component.js index baa643858..61c7c93aa 100644 --- a/ui/components/app/loading-network-screen/loading-network-screen.component.js +++ b/ui/components/app/loading-network-screen/loading-network-screen.component.js @@ -66,6 +66,8 @@ export default class LoadingNetworkScreen extends PureComponent { return t('connectingToSepolia'); case NETWORK_TYPES.LINEA_GOERLI: return t('connectingToLineaGoerli'); + case NETWORK_TYPES.LINEA_MAINNET: + return t('connectingToLineaMainnet'); default: return t('connectingTo', [providerId]); } diff --git a/ui/components/component-library/text/text.stories.tsx b/ui/components/component-library/text/text.stories.tsx index edc3ada37..6ec6cbc75 100644 --- a/ui/components/component-library/text/text.stories.tsx +++ b/ui/components/component-library/text/text.stories.tsx @@ -62,6 +62,9 @@ function renderBackgroundColor(color) { case Color.lineaGoerliInverse: bgColor = BackgroundColor.lineaGoerli; break; + case Color.lineaMainnetInverse: + bgColor = BackgroundColor.lineaMainnet; + break; default: bgColor = null; break; diff --git a/ui/components/multichain/network-list-menu/network-list-menu.js b/ui/components/multichain/network-list-menu/network-list-menu.js index 93d8d9e15..10d856079 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -37,9 +37,16 @@ import { MetaMetricsEventCategory, MetaMetricsEventName, } from '../../../../shared/constants/metametrics'; -import { getCompletedOnboarding } from '../../../ducks/metamask/metamask'; +import { + getCompletedOnboarding, + isLineaMainnetNetworkReleased, +} from '../../../ducks/metamask/metamask'; -const UNREMOVABLE_CHAIN_IDS = [CHAIN_IDS.MAINNET, ...TEST_CHAINS]; +const UNREMOVABLE_CHAIN_IDS = [ + CHAIN_IDS.MAINNET, + CHAIN_IDS.LINEA_MAINNET, + ...TEST_CHAINS, +]; export const NetworkListMenu = ({ onClose }) => { const t = useI18nContext(); @@ -58,6 +65,8 @@ export const NetworkListMenu = ({ onClose }) => { const completedOnboarding = useSelector(getCompletedOnboarding); + const lineaMainnetReleased = useSelector(isLineaMainnetNetworkReleased); + useEffect(() => { if (showTestNetworks && !showTestNetworksRef.current) { // Scroll to the bottom of the list @@ -76,6 +85,12 @@ export const NetworkListMenu = ({ onClose }) => { <> {networks.map((network, index) => { + if ( + !lineaMainnetReleased && + network.providerType === 'linea-mainnet' + ) { + return null; + } const isCurrentNetwork = currentChainId === network.chainId; const canDeleteNetwork = !isCurrentNetwork && diff --git a/ui/components/ui/new-network-info/new-network-info.js b/ui/components/ui/new-network-info/new-network-info.js index bfc487e39..0d449c443 100644 --- a/ui/components/ui/new-network-info/new-network-info.js +++ b/ui/components/ui/new-network-info/new-network-info.js @@ -26,6 +26,7 @@ import Chip from '../chip/chip'; import { setFirstTimeUsedNetwork } from '../../../store/actions'; import { NETWORK_TYPES } from '../../../../shared/constants/network'; import { Icon, IconName, Text } from '../../component-library'; +import { getNetworkLabelKey } from '../../../helpers/utils/i18n-helper'; const NewNetworkInfo = () => { const t = useContext(I18nContext); @@ -95,7 +96,7 @@ const NewNetworkInfo = () => { label={ providerConfig.type === NETWORK_TYPES.RPC ? providerConfig.nickname ?? t('privateNetwork') - : t(providerConfig.type) + : t(getNetworkLabelKey(providerConfig.type)) } labelProps={{ color: Color.textDefault, diff --git a/ui/components/ui/typography/typography.js b/ui/components/ui/typography/typography.js index 7e2a5742b..49f658316 100644 --- a/ui/components/ui/typography/typography.js +++ b/ui/components/ui/typography/typography.js @@ -35,6 +35,8 @@ export const ValidColors = [ Color.sepoliaInverse, Color.lineaGoerli, Color.lineaGoerliInverse, + Color.lineaMainnet, + Color.lineaMainnetInverse, ]; export const ValidTags = [ diff --git a/ui/css/design-system/colors.scss b/ui/css/design-system/colors.scss index 07653a1d5..757582c56 100644 --- a/ui/css/design-system/colors.scss +++ b/ui/css/design-system/colors.scss @@ -43,6 +43,8 @@ $color-map: ( 'sepolia-inverse': --color-network-sepolia-inverse, 'linea-goerli': --color-network-linea-goerli-default, 'linea-goerli-inverse': --color-network-linea-goerli-inverse, + 'linea-mainnet': --color-network-linea-mainnet-default, + 'linea-mainnet-inverse': --color-network-linea-mainnet-inverse, 'localhost': --color-network-localhost-default, 'transparent': --transparent, 'flask-purple': --color-flask-default, diff --git a/ui/css/utilities/colors.scss b/ui/css/utilities/colors.scss index b2ae6f449..a127746e8 100644 --- a/ui/css/utilities/colors.scss +++ b/ui/css/utilities/colors.scss @@ -4,8 +4,10 @@ --mainnet: #29b6af; --inherit: inherit; --transparent: transparent; - --color-network-linea-goerli-default: #000; + --color-network-linea-goerli-default: #61dfff; --color-network-linea-goerli-inverse: #fcfcfc; + --color-network-linea-mainnet-default: #121212; + --color-network-linea-mainnet-inverse: #fcfcfc; // DO NOT CHANGE // Required for the QR reader to work properly --qr-code-white-background: #fff; diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index 0a06147e6..0c853e74d 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -426,3 +426,7 @@ export function doesUserHaveALedgerAccount(state) { return kr.type === KeyringType.ledger; }); } + +export function isLineaMainnetNetworkReleased(state) { + return state.metamask.isLineaMainnetReleased; +} diff --git a/ui/helpers/constants/design-system.ts b/ui/helpers/constants/design-system.ts index f6ceceacd..b13ddf99c 100644 --- a/ui/helpers/constants/design-system.ts +++ b/ui/helpers/constants/design-system.ts @@ -48,6 +48,8 @@ export enum Color { sepolia = 'sepolia', lineaGoerli = 'linea-goerli', lineaGoerliInverse = 'linea-goerli-inverse', + lineaMainnet = 'linea-mainnet', + lineaMainnetInverse = 'linea-mainnet-inverse', transparent = 'transparent', localhost = 'localhost', inherit = 'inherit', @@ -78,6 +80,7 @@ export enum BackgroundColor { goerli = 'goerli', sepolia = 'sepolia', lineaGoerli = 'linea-goerli', + lineaMainnet = 'linea-mainnet', transparent = 'transparent', localhost = 'localhost', } @@ -104,6 +107,7 @@ export enum BorderColor { goerli = 'goerli', sepolia = 'sepolia', lineaGoerli = 'linea-goerli', + lineaMainnet = 'linea-mainnet', transparent = 'transparent', localhost = 'localhost', backgroundDefault = 'background-default', // exception for border color when element is meant to look "cut out" @@ -129,6 +133,8 @@ export enum TextColor { sepolia = 'sepolia', lineaGoerli = 'linea-goerli', lineaGoerliInverse = 'linea-goerli-inverse', + lineaMainnet = 'linea-mainnet', + lineaMainnetInverse = 'linea-mainnet-inverse', goerliInverse = 'goerli-inverse', sepoliaInverse = 'sepolia-inverse', transparent = 'transparent', @@ -154,6 +160,8 @@ export enum IconColor { sepolia = 'sepolia', lineaGoerli = 'linea-goerli', lineaGoerliInverse = 'linea-goerli-inverse', + lineaMainnet = 'linea-mainnet', + lineaMainnetInverse = 'linea-mainnet-inverse', goerliInverse = 'goerli-inverse', sepoliaInverse = 'sepolia-inverse', } diff --git a/ui/helpers/constants/settings.js b/ui/helpers/constants/settings.js index 59245f9f4..4e2096a38 100644 --- a/ui/helpers/constants/settings.js +++ b/ui/helpers/constants/settings.js @@ -226,6 +226,13 @@ export const SETTINGS_CONSTANTS = [ route: `${NETWORKS_ROUTE}#networks-mainnet`, icon: 'fa fa-plug', }, + { + tabMessage: (t) => t('networks'), + sectionMessage: (t) => t('lineaMainnet'), + descriptionMessage: (t) => t('lineaMainnet'), + route: `${NETWORKS_ROUTE}#networks-linea-mainnet`, + icon: 'fa fa-plug', + }, { tabMessage: (t) => t('networks'), sectionMessage: (t) => t('goerli'), diff --git a/ui/helpers/utils/i18n-helper.tsx b/ui/helpers/utils/i18n-helper.tsx index 6f8bc478f..59c28d1e5 100644 --- a/ui/helpers/utils/i18n-helper.tsx +++ b/ui/helpers/utils/i18n-helper.tsx @@ -171,5 +171,8 @@ export function getNetworkLabelKey(network: string): string { if (network === NETWORK_TYPES.LINEA_GOERLI) { return 'lineaGoerli'; } + if (network === NETWORK_TYPES.LINEA_MAINNET) { + return 'lineaMainnet'; + } return network; } diff --git a/ui/helpers/utils/settings-search.test.js b/ui/helpers/utils/settings-search.test.js index 9f8a97f08..07a7f3aa5 100644 --- a/ui/helpers/utils/settings-search.test.js +++ b/ui/helpers/utils/settings-search.test.js @@ -173,7 +173,7 @@ describe('Settings Search Utils', () => { }); it('should get good network section number', () => { - expect(getNumberOfSettingsInSection(t, t('networks'))).toStrictEqual(5); + expect(getNumberOfSettingsInSection(t, t('networks'))).toStrictEqual(6); }); it('should get good experimental section number', () => { diff --git a/ui/helpers/utils/util.js b/ui/helpers/utils/util.js index c6919fb9a..5f0030ed3 100644 --- a/ui/helpers/utils/util.js +++ b/ui/helpers/utils/util.js @@ -62,6 +62,7 @@ export function isDefaultMetaMaskChain(chainId) { if ( !chainId || chainId === CHAIN_IDS.MAINNET || + chainId === CHAIN_IDS.LINEA_MAINNET || chainId === CHAIN_IDS.GOERLI || chainId === CHAIN_IDS.SEPOLIA || chainId === CHAIN_IDS.LINEA_GOERLI || diff --git a/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js index ef0af95e0..c25e097fc 100644 --- a/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js +++ b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js @@ -426,7 +426,9 @@ export default class ConfirmApproveContent extends Component { this.props; const useBlockExplorer = rpcPrefs?.blockExplorerUrl || - [...TEST_CHAINS, CHAIN_IDS.MAINNET].includes(chainId); + [...TEST_CHAINS, CHAIN_IDS.MAINNET, CHAIN_IDS.LINEA_MAINNET].includes( + chainId, + ); const titleTokenDescription = this.getTokenName(); const tokenIdWrapped = tokenId ? ` (#${tokenId})` : ''; diff --git a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.js b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.js index bdd52e21b..94a64145a 100644 --- a/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.js +++ b/ui/pages/confirm-token-transaction-base/confirm-token-transaction-base.js @@ -64,7 +64,9 @@ export default function ConfirmTokenTransactionBase({ const getTitleTokenDescription = (renderType) => { const useBlockExplorer = rpcPrefs?.blockExplorerUrl || - [...TEST_CHAINS, CHAIN_IDS.MAINNET].includes(chainId); + [...TEST_CHAINS, CHAIN_IDS.MAINNET, CHAIN_IDS.LINEA_MAINNET].includes( + chainId, + ); const nftCollection = nftCollections.find( (collection) => diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index f9adb501b..97d03d0ca 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -601,6 +601,8 @@ export default class Routes extends Component { return t('connectingToSepolia'); case NETWORK_TYPES.LINEA_GOERLI: return t('connectingToLineaGoerli'); + case NETWORK_TYPES.LINEA_MAINNET: + return t('connectingToLineaMainnet'); default: return t('connectingTo', [providerId]); } diff --git a/ui/pages/settings/networks-tab/networks-list-item/networks-list-item.js b/ui/pages/settings/networks-tab/networks-list-item/networks-list-item.js index 3256642a3..2bfbc0311 100644 --- a/ui/pages/settings/networks-tab/networks-list-item/networks-list-item.js +++ b/ui/pages/settings/networks-tab/networks-list-item/networks-list-item.js @@ -97,13 +97,13 @@ const NetworksListItem = ({ ) )} {network.isATestNetwork && network.chainId !== CHAIN_IDS.LINEA_GOERLI && ( { const networksTabSelectedNetworkConfigurationId = useSelector( getNetworksTabSelectedNetworkConfigurationId, ); + const isLineaMainnetReleased = useSelector(isLineaMainnetNetworkReleased); const networkConfigurationsList = Object.entries(networkConfigurations).map( ([networkConfigurationId, networkConfiguration]) => { @@ -69,8 +74,14 @@ const NetworksTab = ({ addNewNetwork }) => { }, ); - const networksToRender = [...defaultNetworks, ...networkConfigurationsList]; - + let networksToRender = [...defaultNetworks, ...networkConfigurationsList]; + if (!isLineaMainnetReleased) { + networksToRender = networksToRender.filter( + (network) => + network.chainId !== + BUILT_IN_NETWORKS[NETWORK_TYPES.LINEA_MAINNET].chainId, + ); + } let selectedNetwork = networksToRender.find( (network) => diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index ce0054015..c6b736878 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -34,6 +34,8 @@ import { CURRENCY_SYMBOLS, TEST_NETWORK_TICKER_MAP, LINEA_GOERLI_TOKEN_IMAGE_URL, + LINEA_MAINNET_TOKEN_IMAGE_URL, + LINEA_MAINNET_DISPLAY_NAME, } from '../../shared/constants/network'; import { WebHIDConnectedStatuses, @@ -1198,6 +1200,16 @@ export function getAllNetworks(state) { providerType: NETWORK_TYPES.MAINNET, ticker: CURRENCY_SYMBOLS.ETH, }, + { + chainId: CHAIN_IDS.LINEA_MAINNET, + nickname: LINEA_MAINNET_DISPLAY_NAME, + rpcUrl: CHAIN_ID_TO_RPC_URL_MAP[CHAIN_IDS.LINEA_MAINNET], + rpcPrefs: { + imageUrl: LINEA_MAINNET_TOKEN_IMAGE_URL, + }, + providerType: NETWORK_TYPES.LINEA_MAINNET, + ticker: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.LINEA_MAINNET], + }, // Custom networks added by the user ...Object.values(networkConfigurations).filter( ({ chainId }) => ![CHAIN_IDS.LOCALHOST].includes(chainId), diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js index f4a0dd783..8819a9769 100644 --- a/ui/selectors/selectors.test.js +++ b/ui/selectors/selectors.test.js @@ -7,6 +7,14 @@ import { } from '../../shared/constants/network'; import * as selectors from './selectors'; +jest.mock('../../shared/modules/network.utils', () => { + const actual = jest.requireActual('../../shared/modules/network.utils'); + return { + ...actual, + shouldShowLineaMainnet: jest.fn().mockResolvedValue(true), + }; +}); + describe('Selectors', () => { describe('#getSelectedAddress', () => { it('returns undefined if selectedAddress is undefined', () => { @@ -317,7 +325,7 @@ describe('Selectors', () => { }, }, }); - expect(networks).toHaveLength(1); + expect(networks).toHaveLength(2); }); it('returns networks with showTestNetworks on', () => { @@ -328,7 +336,7 @@ describe('Selectors', () => { }, }, }); - expect(networks.length).toBeGreaterThan(1); + expect(networks.length).toBeGreaterThan(2); }); });