Skip to content

Commit

Permalink
Merge branch 'main' into DX-2625/port-core-sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeSchwert authored Feb 28, 2024
2 parents 0ac07e4 + 45ec7fc commit 62cf68b
Show file tree
Hide file tree
Showing 20 changed files with 551 additions and 70 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import {
Body, Box, Button, CloudImage, Drawer, Heading, Logo,
} from '@biom3/react';
import { Web3Provider } from '@ethersproject/providers';
import { ChainId, Checkout } from '@imtbl/checkout-sdk';
import { Environment } from '@imtbl/config';
import { FooterLogo } from 'components/Footer/FooterLogo';
import { getL1ChainId } from 'lib';
import { getChainNameById } from 'lib/chains';
import {
isMetaMaskProvider,
isWalletConnectProvider,
} from 'lib/providerUtils';
import { getRemoteImage } from 'lib/utils';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';

export interface NetworkSwitchDrawerProps {
visible: boolean;
targetChainId: ChainId;
provider: Web3Provider;
checkout: Checkout;
onCloseDrawer: () => void;
onNetworkSwitch?: (provider: Web3Provider) => void;
}
export function NetworkSwitchDrawer({
visible,
targetChainId,
provider,
checkout,
onCloseDrawer,
onNetworkSwitch,
}: NetworkSwitchDrawerProps) {
const { t } = useTranslation();

const targetChainName = getChainNameById(targetChainId);
const networkSwitchImage = useMemo(() => {
if (targetChainId === getL1ChainId(checkout.config)) {
const ethNetworkImageUrl = getRemoteImage(Environment.SANDBOX, '/switchnetworkethereum.svg');
return <CloudImage imageUrl={ethNetworkImageUrl} sx={{ width: '161px', height: '98px' }} />;
}
return <Logo logo="ImmutableSymbol" sx={{ fill: 'base.color.accent.1', width: 'base.spacing.x20' }} />;
}, [targetChainId]);

const handleSwitchNetwork = useCallback(async () => {
if (!checkout) return;
const switchNetworkResult = await checkout.switchNetwork({
provider,
chainId: targetChainId,
});
if (onNetworkSwitch) {
onNetworkSwitch(switchNetworkResult.provider);
}
}, [checkout, provider, onNetworkSwitch, targetChainId]);

const isWalletConnect = isWalletConnectProvider(provider);

const walletConnectPeerName = useMemo(() => {
if (!isWalletConnect) return '';
return (provider.provider as any)?.session?.peer?.metadata?.name as string;
}, [provider, isWalletConnect]);

const isMetaMaskMobileWalletPeer = useMemo(
() => walletConnectPeerName?.toLowerCase().includes('metamask'),
[walletConnectPeerName],
);

const walletDisplayName = useMemo(() => {
if (isMetaMaskProvider(provider)) return 'MetaMask wallet';
if (isWalletConnect && walletConnectPeerName) return walletConnectPeerName;
return 'wallet';
}, [provider, isWalletConnect, walletConnectPeerName]);

const requireManualSwitch = isWalletConnect && isMetaMaskMobileWalletPeer;

return (
<Drawer
size="threeQuarter"
visible={visible}
onCloseDrawer={onCloseDrawer}
showHeaderBar
headerBarTitle=""
>
<Drawer.Content sx={{
paddingX: 'base.spacing.x4',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<Box sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 'base.spacing.x4',
paddingX: 'base.spacing.x2',
}}
>
{networkSwitchImage}
<Heading size="small" weight="bold" sx={{ textAlign: 'center', paddingX: 'base.spacing.x6' }}>
{t('drawers.networkSwitch.heading', {
wallet: walletDisplayName,
})}
</Heading>
{/** MetaMask mobile requires manual switch */}
{requireManualSwitch && (
<Body size="large" weight="regular" sx={{ textAlign: 'center' }}>
{t('drawers.networkSwitch.manualSwitch.body', {
chain: targetChainName,
})}
</Body>
)}
{!requireManualSwitch && (
<Body size="large" weight="regular" sx={{ textAlign: 'center' }}>
{t('drawers.networkSwitch.controlledSwitch.body', {
chain: targetChainName,
})}
</Body>
)}

</Box>

<Box sx={{
display: 'flex',
flexDirection: 'column',
paddingX: 'base.spacing.x4',
width: '100%',
}}
>
{!requireManualSwitch && (
<Button
size="large"
variant="primary"
sx={{ width: '100%', marginBottom: 'base.spacing.x2' }}
onClick={handleSwitchNetwork}
>
{t('drawers.networkSwitch.switchButton', {
chain: targetChainName,
})}
</Button>
)}
<FooterLogo />
</Box>
</Drawer.Content>
</Drawer>
);
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import {
Box, Button, EllipsizedText, Logo,
Box, Button, EllipsizedText, FramedImage, Logo,
} from '@biom3/react';
import { useContext } from 'react';
import { useContext, useEffect, useState } from 'react';
import { BridgeContext } from 'widgets/bridge/context/BridgeContext';
import { getWalletProviderNameByProvider } from 'lib/providerUtils';
import { getWalletProviderNameByProvider, isWalletConnectProvider } from 'lib/providerUtils';
import {
UserJourney,
useAnalytics,
} from 'context/analytics-provider/SegmentAnalyticsProvider';
import { BridgeWidgetViews } from 'context/view-context/BridgeViewContextTypes';
import { useTranslation } from 'react-i18next';
import { getWalletLogoByName } from 'lib/logoUtils';
import { headingStyles } from './ChangeWalletStyles';
import { useWalletConnect } from 'lib/hooks/useWalletConnect';
import {
headingStyles, wcStickerLogoStyles, wcWalletLogoStyles, wcWalletLogoWrapperStyles,
} from './ChangeWalletStyles';

export interface ChangeWalletProps {
onChangeWalletClick: () => void;
Expand All @@ -20,8 +23,15 @@ export interface ChangeWalletProps {
export function ChangeWallet({ onChangeWalletClick }: ChangeWalletProps) {
const { t } = useTranslation();
const {
bridgeState: { from },
bridgeState: { checkout, from },
} = useContext(BridgeContext);
const [walletLogoUrl, setWalletLogoUrl] = useState<string | undefined>(
undefined,
);
const [isWalletConnect, setIsWalletConnect] = useState<boolean>(false);
const { isWalletConnectEnabled, getWalletLogoUrl } = useWalletConnect({
checkout,
});
const { track } = useAnalytics();
const walletAddress = from?.walletAddress || '';

Expand All @@ -39,18 +49,38 @@ export function ChangeWallet({ onChangeWalletClick }: ChangeWalletProps) {
onChangeWalletClick();
};

useEffect(() => {
if (isWalletConnectEnabled) {
setIsWalletConnect(isWalletConnectProvider(from?.web3Provider));
(async () => {
setWalletLogoUrl(await getWalletLogoUrl());
})();
}
}, [isWalletConnectEnabled, from]);

return (
<Box sx={headingStyles}>
<Box
sx={{ display: 'flex', alignItems: 'center', gap: 'base.spacing.x1' }}
>
<Logo
logo={walletLogo}
sx={{
width: 'base.icon.size.400',
pr: 'base.spacing.x1',
}}
/>
{isWalletConnect && walletLogoUrl ? (
<Box sx={wcWalletLogoWrapperStyles}>
<FramedImage
imageUrl={walletLogoUrl}
alt="walletconnect"
sx={wcWalletLogoStyles}
/>
<Logo logo="WalletConnectSymbol" sx={wcStickerLogoStyles} />
</Box>
) : (
<Logo
logo={walletLogo}
sx={{
width: 'base.icon.size.400',
pr: 'base.spacing.x1',
}}
/>
)}
<EllipsizedText
leftSideLength={6}
rightSideLength={4}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,22 @@ export const headingStyles = {
p: 'base.spacing.x2',
pb: 'base.spacing.x4',
};

export const wcWalletLogoWrapperStyles = {
position: 'relative',
pr: 'base.spacing.x2',
};

export const wcWalletLogoStyles = {
width: 'base.icon.size.400',
};

export const wcStickerLogoStyles = {
position: 'absolute',
top: '-6px',
left: '18px',
width: '20px',
padding: '2px',
backgroundColor: 'base.color.translucent.inverse.900',
borderRadius: 'base.borderRadius.x2',
};
6 changes: 4 additions & 2 deletions packages/checkout/widgets-lib/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { WalletConnectManager } from 'lib/walletConnect';
import {
sendProviderUpdatedEvent,
addProviderListenersForWidgetRoot,
DEFAULT_THEME,
} from './lib';
import './i18n';

Expand All @@ -28,12 +29,13 @@ export class WidgetsFactory implements IWidgetsFactory {
constructor(sdk: Checkout, widgetConfig: WidgetConfiguration) {
this.sdk = sdk;
this.widgetConfig = widgetConfig;
if (widgetConfig.walletConnect && widgetConfig.theme) {
if (!this.widgetConfig.theme) this.widgetConfig.theme = DEFAULT_THEME;
if (widgetConfig.walletConnect) {
try {
WalletConnectManager.getInstance().initialise(
sdk.config.environment,
widgetConfig.walletConnect,
widgetConfig.theme,
this.widgetConfig.theme,
);
} catch (err) {
// eslint-disable-next-line no-console
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const useWalletConnect = ({ checkout }: UseWalletConnectParams) => {
// eslint-disable-next-line no-console
console.log('activate succeeded but there is no connected session');
}
// eslint-disable-next-line no-console
// eslint-disable-next-line no-console
}).catch((err) => console.log('activate existing pairing error', err));
}
} catch (err) {
Expand Down Expand Up @@ -140,11 +140,14 @@ export const useWalletConnect = ({ checkout }: UseWalletConnectParams) => {
})
), [ethereumProvider, walletConnectModal]);

const getWalletLogoUrl = useCallback(async () => await WalletConnectManager.getInstance().getWalletLogoUrl(), []);

return {
isWalletConnectEnabled,
ethereumProvider,
walletConnectBusy,
walletConnectModal,
openWalletConnectModal,
getWalletLogoUrl,
};
};
50 changes: 46 additions & 4 deletions packages/checkout/widgets-lib/src/lib/walletConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const darkThemeVariables = {
'--wcm-container-border-radius': '8px',
// eslint-disable-next-line @typescript-eslint/naming-convention
'--wcm-wallet-icon-border-radius': '8px',
// eslint-disable-next-line @typescript-eslint/naming-convention
'--wcm-overlay-background-color': 'rgba(255, 255, 255, 0.1)',
};

const lightThemeVariables = {
Expand All @@ -36,8 +38,17 @@ const lightThemeVariables = {
'--wcm-container-border-radius': '8px',
// eslint-disable-next-line @typescript-eslint/naming-convention
'--wcm-wallet-icon-border-radius': '8px',
// eslint-disable-next-line @typescript-eslint/naming-convention
'--wcm-overlay-background-color': 'rgba(255, 255, 255, 0.1)',
};

// Whitelisted wallet ids on WalletConnect explorer API
const metamaskId = 'c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96';
const frontierId = '85db431492aa2e8672e93f4ea7acf10c88b97b867b0d373107af63dc4880f041';
const coinbaseId = 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa';
// const phantomId = 'a797aa35c0fadbfc1a53e7f675162ed5226968b44a19ee3d24385c64d1d3c393';
// const rainbowId = '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369';

export class WalletConnectManager {
private static instance: WalletConnectManager;

Expand All @@ -53,6 +64,8 @@ export class WalletConnectManager {

private ethereumProvider!: EthereumProvider;

private walletListings!: any;

private validateConfig(config: WalletConnectConfiguration): boolean {
if (!config.projectId || config.projectId === '') {
// eslint-disable-next-line no-console
Expand Down Expand Up @@ -100,10 +113,9 @@ export class WalletConnectManager {
projectId: this.walletConnectConfig.projectId,
chains: this.environment === Environment.PRODUCTION ? productionModalChains : testnetModalChains,
explorerRecommendedWalletIds: [
'c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96', // MetaMask
'85db431492aa2e8672e93f4ea7acf10c88b97b867b0d373107af63dc4880f041', // Frontier
// 'a797aa35c0fadbfc1a53e7f675162ed5226968b44a19ee3d24385c64d1d3c393', // Phantom
// '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369', // Rainbow
metamaskId,
frontierId,
coinbaseId,
],
explorerExcludedWalletIds: 'ALL',
themeMode: this.theme,
Expand Down Expand Up @@ -149,4 +161,34 @@ export class WalletConnectManager {
}
});
}

private async loadWalletListings(): Promise<Response | undefined> {
// eslint-disable-next-line max-len
const walletListingsApi = `https://explorer-api.walletconnect.com/v3/wallets?projectId=${this.walletConnectConfig.projectId}&ids=${metamaskId},${frontierId},${coinbaseId}`;
try {
const response = await fetch(walletListingsApi);
const data = await response.json();
return data;
} catch (error) {
// eslint-disable-next-line no-console
console.error('Error fetching wallet listings', error);
}
return undefined;
}

public async getWalletLogoUrl(): Promise<string | undefined> {
if (!this.walletListings) {
this.walletListings = await this.loadWalletListings();
}
const walletName = this.ethereumProvider?.session?.peer.metadata.name;

if (!this.walletListings || !walletName) {
return undefined;
}

const matchedWallet = Object.values(this.walletListings.listings)
.find((wallet: any) => walletName.toLowerCase().includes(wallet.slug)) as any;

return matchedWallet.image_url.md;
}
}
Loading

0 comments on commit 62cf68b

Please sign in to comment.