Skip to content

Commit

Permalink
feat(551): add Linea Mainnet (#6496)
Browse files Browse the repository at this point in the history
* fix: refactor linea testnet implementation

* fix: image icon issue for linea goerli network

* feat: add new linea mainnet network

* feat: add feature toggle for linea mainnet

* fix: import issue

* fix: update linea goerli color

* feat: add linea mainnet logo

* update network tests

* fix: image icons issue

* fix: display linea mainnet logo in header

* fix: refactor linea mainnet feature toggle function

* fix: update linea mainnet feature flag date

* remove http request for linea mainnet feature flag

---------

Co-authored-by: sethkfman <10342624+sethkfman@users.noreply.github.com>
  • Loading branch information
VGau and sethkfman authored Jun 20, 2023
1 parent d4598b8 commit 9300ff9
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 32 deletions.
4 changes: 4 additions & 0 deletions app/components/UI/Tokens/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
getDecimalChainId,
getNetworkNameFromProvider,
getTestNetImageByChainId,
isLineaMainnetByChainId,
isMainnetByChainId,
isTestNet,
} from '../../../util/networks';
Expand Down Expand Up @@ -261,12 +262,15 @@ const Tokens: React.FC<TokensI> = ({ tokens }) => {
asset = { ...asset, balanceFiat };

const isMainnet = isMainnetByChainId(chainId);
const isLineaMainnet = isLineaMainnetByChainId(chainId);

const NetworkBadgeSource = () => {
if (isTestNet(chainId)) return getTestNetImageByChainId(chainId);

if (isMainnet) return images.ETHEREUM;

if (isLineaMainnet) return images['LINEA-MAINNET'];

return images[ticker];
};

Expand Down
34 changes: 31 additions & 3 deletions app/components/Views/NetworkSelector/NetworkSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Third party dependencies.
import React, { useRef } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { Platform } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import images from 'images/image-icons';
Expand All @@ -23,9 +23,10 @@ import Networks, {
compareRpcUrls,
getAllNetworks,
getNetworkImageSource,
shouldShowLineaMainnetNetwork,
} from '../../../util/networks';
import { EngineState } from 'app/selectors/types';
import { MAINNET } from '../../../constants/network';
import { LINEA_MAINNET, MAINNET } from '../../../constants/network';
import Button from '../../../component-library/components/Buttons/Button/Button';
import {
ButtonSize,
Expand All @@ -51,13 +52,22 @@ const NetworkSelector = () => {
const thirdPartyApiMode = useSelector(
(state: any) => state.privacy.thirdPartyApiMode,
);
const [lineaMainnetReleased, setLineaMainnetReleased] = useState(false);

const providerConfig: ProviderConfig = useSelector(selectProviderConfig);
const frequentRpcList: FrequentRpc[] = useSelector(
(state: EngineState) =>
state.engine.backgroundState.PreferencesController.frequentRpcList,
);

useEffect(() => {
const shouldShowLineaMainnet = shouldShowLineaMainnetNetwork();

if (shouldShowLineaMainnet) {
setLineaMainnetReleased(shouldShowLineaMainnet);
}
}, []);

const onNetworkChange = (type: string) => {
const { NetworkController, CurrencyRateController } = Engine.context;
CurrencyRateController.setNativeCurrency('ETH');
Expand Down Expand Up @@ -122,6 +132,23 @@ const NetworkSelector = () => {
);
};

const renderLineaMainnet = () => {
const { name: lineaMainnetName, chainId } = Networks['linea-mainnet'];
return (
<Cell
variant={CellVariants.Select}
title={lineaMainnetName}
avatarProps={{
variant: AvatarVariants.Network,
name: lineaMainnetName,
imageSource: images['LINEA-MAINNET'],
}}
isSelected={chainId.toString() === providerConfig.chainId}
onPress={() => onNetworkChange(LINEA_MAINNET)}
/>
);
};

const renderRpcNetworks = () =>
frequentRpcList.map(
({
Expand Down Expand Up @@ -159,7 +186,7 @@ const NetworkSelector = () => {
);

const renderOtherNetworks = () => {
const getOtherNetworks = () => getAllNetworks().slice(1);
const getOtherNetworks = () => getAllNetworks().slice(2);
return getOtherNetworks().map((network) => {
const { name, imageSource, chainId, networkType } = Networks[network];

Expand Down Expand Up @@ -193,6 +220,7 @@ const NetworkSelector = () => {
<SheetHeader title={strings('networks.select_network')} />
<ScrollView {...generateTestId(Platform, NETWORK_SCROLL_ID)}>
{renderMainnet()}
{lineaMainnetReleased && renderLineaMainnet()}
{renderRpcNetworks()}
{renderOtherNetworks()}
</ScrollView>
Expand Down
52 changes: 44 additions & 8 deletions app/components/Views/Settings/NetworksSettings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import Networks, {
getAllNetworks,
getNetworkImageSource,
isDefaultMainnet,
isLineaMainnet,
shouldShowLineaMainnetNetwork,
} from '../../../../util/networks';
import StyledButton from '../../../UI/StyledButton';
import Engine from '../../../../core/Engine';
import { MAINNET, RPC } from '../../../../constants/network';
import { LINEA_MAINNET, MAINNET, RPC } from '../../../../constants/network';
import FontAwesome from 'react-native-vector-icons/FontAwesome';
import { mockTheme, ThemeContext } from '../../../../util/theme';
import ImageIcons from '../../../UI/ImageIcon';
Expand Down Expand Up @@ -138,6 +140,7 @@ class NetworksSettings extends PureComponent {
state = {
searchString: '',
filteredNetworks: [],
lineaMainnetReleased: false,
};

updateNavBar = () => {
Expand All @@ -154,14 +157,17 @@ class NetworksSettings extends PureComponent {
};

componentDidMount = () => {
const shouldShowLineaMainnet = shouldShowLineaMainnetNetwork();

this.setState({ lineaMainnetReleased: shouldShowLineaMainnet });
this.updateNavBar();
};

componentDidUpdate = () => {
this.updateNavBar();
};

getOtherNetworks = () => getAllNetworks().slice(1);
getOtherNetworks = () => getAllNetworks().slice(2);

onNetworkPress = (network) => {
const { navigation } = this.props;
Expand Down Expand Up @@ -217,6 +223,8 @@ class NetworksSettings extends PureComponent {
// Do not change. This logic must check for 'mainnet' and is used for rendering the out of the box mainnet when searching.
isDefaultMainnet(network) ? (
this.renderMainnet()
) : isLineaMainnet(network) ? (
this.renderLineaMainnet()
) : (
<TouchableOpacity
key={`network-${i}`}
Expand Down Expand Up @@ -326,6 +334,35 @@ class NetworksSettings extends PureComponent {
);
}

renderLineaMainnet() {
const { name: lineaMainnetName } = Networks['linea-mainnet'];
const colors = this.context.colors || mockTheme.colors;
const styles = createStyles(colors);

return (
<View style={styles.mainnetHeader}>
<TouchableOpacity
style={styles.network}
key={`network-${LINEA_MAINNET}`}
onPress={() => this.onNetworkPress(LINEA_MAINNET)}
>
<View style={styles.networkWrapper}>
<ImageIcons image="LINEA-MAINNET" style={styles.networkIcon} />
<View style={styles.networkInfo}>
<Text style={styles.networkLabel}>{lineaMainnetName}</Text>
</View>
</View>
<FontAwesome
name="lock"
size={20}
color={colors.icon.default}
style={styles.icon}
/>
</TouchableOpacity>
</View>
);
}

handleSearchTextChange = (text) => {
this.setState({ searchString: text });
const defaultNetwork = getAllNetworks().map((network, i) => {
Expand Down Expand Up @@ -359,12 +396,10 @@ class NetworksSettings extends PureComponent {
return this.state.filteredNetworks.map((data, i) => {
const { network, chainId, name, color, isCustomRPC } = data;
const image = getNetworkImageSource({ chainId });
return this.networkElement(
name,
image || color,
i,
network,
isCustomRPC,
return (
// TODO: remove this check when linea mainnet is ready
network !== LINEA_MAINNET &&
this.networkElement(name, image || color, i, network, isCustomRPC)
);
});
}
Expand Down Expand Up @@ -413,6 +448,7 @@ class NetworksSettings extends PureComponent {
{strings('app_settings.mainnet')}
</Text>
{this.renderMainnet()}
{this.state.lineaMainnetReleased && this.renderLineaMainnet()}
{this.renderRpcNetworksView()}
<Text style={styles.sectionLabel}>
{strings('app_settings.test_network_name')}
Expand Down
2 changes: 2 additions & 0 deletions app/constants/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const HOMESTEAD = 'homestead';
export const GOERLI = 'goerli';
export const SEPOLIA = 'sepolia';
export const LINEA_GOERLI = 'linea-goerli';
export const LINEA_MAINNET = 'linea-mainnet';
export const RPC = 'rpc';
export const NO_RPC_BLOCK_EXPLORER = 'NO_BLOCK_EXPLORER';
export const PRIVATENETWORK = 'PRIVATENETWORK';
Expand All @@ -24,4 +25,5 @@ export const NETWORKS_CHAIN_ID = {
SEPOLIA: '11155111',
LINEA_GOERLI: '59140',
GOERLI: '5',
LINEA_MAINNET: '59144',
};
3 changes: 2 additions & 1 deletion app/constants/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export const MIXPANEL_ENDPOINT_BASE_URL = 'https://mixpanel.com/api/app';
export const CHAINLIST_URL = 'https://chainlist.wtf';
export const MM_ETHERSCAN_URL = 'https://etherscamdb.info/domain/meta-mask.com';
export const LINEA_GOERLI_BLOCK_EXPLORER = 'https://goerli.lineascan.build';

export const LINEA_MAINNET_BLOCK_EXPLORER = 'https://lineascan.build';
export const LINEA_MAINNET_RPC_URL = `https://linea-mainnet.infura.io/v3/${process.env.MM_INFURA_PROJECT_ID}`;
// Phishing
export const MM_PHISH_DETECT_URL =
'https://github.com/metamask/eth-phishing-detect';
Expand Down
2 changes: 2 additions & 0 deletions app/images/image-icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import AVAX from './avalanche.png';
import GOERLI from './goerli-logo-dark.png';
import LINEA_GOERLI from './linea-testnet-logo.png';
import SEPOLIA from './sepolia-logo-dark.png';
import LINEA_MAINNET from './linea-mainnet-logo.png';

export default {
PALM,
Expand All @@ -24,4 +25,5 @@ export default {
GOERLI,
'LINEA-GOERLI': LINEA_GOERLI,
SEPOLIA,
'LINEA-MAINNET': LINEA_MAINNET,
};
Binary file added app/images/linea-mainnet-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 6 additions & 2 deletions app/util/etherscan.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { LINEA_GOERLI_BLOCK_EXPLORER } from '../constants/urls';
import { LINEA_GOERLI, MAINNET } from '../constants/network';
import {
LINEA_GOERLI_BLOCK_EXPLORER,
LINEA_MAINNET_BLOCK_EXPLORER,
} from '../constants/urls';
import { LINEA_GOERLI, LINEA_MAINNET, MAINNET } from '../constants/network';

/**
* Gets the etherscan link for an address in a specific network
Expand Down Expand Up @@ -31,6 +34,7 @@ export function getEtherscanTransactionUrl(network, tx_hash) {
*/
export function getEtherscanBaseUrl(network) {
if (network === LINEA_GOERLI) return LINEA_GOERLI_BLOCK_EXPLORER;
if (network === LINEA_MAINNET) return LINEA_MAINNET_BLOCK_EXPLORER;
const subdomain =
network.toLowerCase() === MAINNET ? '' : `${network.toLowerCase()}.`;
return `https://${subdomain}etherscan.io`;
Expand Down
33 changes: 30 additions & 3 deletions app/util/networks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
SEPOLIA,
RPC,
LINEA_GOERLI,
LINEA_MAINNET,
} from '../../../app/constants/network';
import { NetworkSwitchErrorType } from '../../../app/constants/error';
import { query } from '@metamask/controller-utils';
Expand All @@ -27,7 +28,8 @@ export { handleNetworkSwitch };
const ethLogo = require('../../images/eth-logo-new.png');
const goerliLogo = require('../../images/goerli-logo-dark.png');
const sepoliaLogo = require('../../images/sepolia-logo-dark.png');
const lineaLogo = require('../../images/linea-testnet-logo.png');
const lineaGoerliLogo = require('../../images/linea-testnet-logo.png');
const lineaMainnetLogo = require('../../images/linea-mainnet-logo.png');

/* eslint-enable */
import PopularList from './customNetworks';
Expand Down Expand Up @@ -56,6 +58,16 @@ const NetworkList = {
networkType: 'mainnet',
imageSource: ethLogo,
},
[LINEA_MAINNET]: {
name: 'Linea Main Network',
shortName: 'Linea',
networkId: 59144,
chainId: 59144,
hexChainId: '0xe708',
color: '#121212',
networkType: 'linea-mainnet',
imageSource: lineaMainnetLogo,
},
[GOERLI]: {
name: 'Goerli Test Network',
shortName: 'Goerli',
Expand All @@ -82,9 +94,9 @@ const NetworkList = {
networkId: 59140,
chainId: 59140,
hexChainId: '0xe704',
color: '#121212',
color: '#61dfff',
networkType: 'linea-goerli',
imageSource: lineaLogo,
imageSource: lineaGoerliLogo,
},
[RPC]: {
name: 'Private Network',
Expand Down Expand Up @@ -112,6 +124,8 @@ export const isDefaultMainnet = (networkType) => networkType === MAINNET;
export const isMainNet = (network) =>
isDefaultMainnet(network?.providerConfig?.type) || network === String(1);

export const isLineaMainnet = (networkType) => networkType === LINEA_MAINNET;

export const getDecimalChainId = (chainId) => {
if (!chainId || typeof chainId !== 'string' || !chainId.startsWith('0x')) {
return chainId;
Expand All @@ -122,6 +136,9 @@ export const getDecimalChainId = (chainId) => {
export const isMainnetByChainId = (chainId) =>
getDecimalChainId(String(chainId)) === String(1);

export const isLineaMainnetByChainId = (chainId) =>
getDecimalChainId(String(chainId)) === String(59144);

export const isMultiLayerFeeNetwork = (chainId) =>
chainId === NETWORKS_CHAIN_ID.OPTIMISM;

Expand Down Expand Up @@ -353,9 +370,16 @@ export const getNetworkNameFromProvider = (provider) => {
export const getNetworkImageSource = ({ networkType, chainId }) => {
const defaultNetwork = getDefaultNetworkByChainId(chainId);
const isDefaultEthMainnet = isDefaultMainnet(networkType);
const isLineaMainnetNetwork = isLineaMainnet(networkType);

if (defaultNetwork && isDefaultEthMainnet) {
return defaultNetwork.imageSource;
}

if (defaultNetwork && isLineaMainnetNetwork) {
return defaultNetwork.imageSource;
}

const popularNetwork = PopularList.find(
(network) => network.chainId === chainId,
);
Expand Down Expand Up @@ -455,3 +479,6 @@ export const getBlockExplorerTxUrl = (
*/
export const getIsNetworkOnboarded = (chainId, networkOnboardedState) =>
networkOnboardedState[chainId];

export const shouldShowLineaMainnetNetwork = () =>
new Date().getTime() > Date.UTC(2023, 6, 11, 18);
Loading

0 comments on commit 9300ff9

Please sign in to comment.