Skip to content

Commit

Permalink
Feat/wt 1932 show bridge form (#1218)
Browse files Browse the repository at this point in the history
Co-authored-by: Zach Couchman <[email protected]>
  • Loading branch information
ZacharyCouchman and ZacharyCouchman authored Nov 30, 2023
1 parent e9017bf commit 6caf39e
Show file tree
Hide file tree
Showing 17 changed files with 371 additions and 182 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ViewType } from './ViewType';

export enum XBridgeWidgetViews {
BRIDGE_WALLET_SELECTION = 'BRIDGE_WALLET_SELECTION',
WALLET_NETWORK_SECLECTION = 'WALLET_NETWORK_SECLECTION',
BRIDGE_FORM = 'BRIDGE_FORM',
BRIDGE_REVIEW = 'BRIDGE_REVIEW',
}
Expand All @@ -12,7 +12,7 @@ export type XBridgeWidgetView =
| XBridgeReview;

interface XBridgeCrossWalletSelection extends ViewType {
type: XBridgeWidgetViews.BRIDGE_WALLET_SELECTION,
type: XBridgeWidgetViews.WALLET_NETWORK_SECLECTION,
data?: {}
}

Expand Down
15 changes: 15 additions & 0 deletions packages/checkout/widgets-lib/src/lib/addressUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { abbreviateAddress } from './addressUtils';

describe('addressUtils', () => {
it('should abbrviate address to first 6 chars, ... then last 4 chars', () => {
expect(abbreviateAddress('0x1234567890')).toBe('0x1234...7890');
});

it('should return empty string when no address provided', () => {
expect(abbreviateAddress('')).toBe('');
});

it('should return empty string when undefined is passed', () => {
expect(abbreviateAddress(undefined as any)).toBe('');
});
});
4 changes: 4 additions & 0 deletions packages/checkout/widgets-lib/src/lib/addressUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export function abbreviateAddress(address: string) {
if (!address) return '';
return address.substring(0, 6).concat('...').concat(address.substring(address.length - 4));
}
Original file line number Diff line number Diff line change
Expand Up @@ -442,16 +442,16 @@ export const text = {
text: 'Order completed',
actionText: 'Continue',
},
[XBridgeWidgetViews.BRIDGE_WALLET_SELECTION]: {
[XBridgeWidgetViews.WALLET_NETWORK_SECLECTION]: {
layoutHeading: 'Move',
heading: 'Where would you like to move funds between?',
from: {
fromFormInput: {
heading: 'From',
selectDefaultText: 'Select wallet and network',
walletSelectorHeading: 'From wallet',
networkSelectorHeading: 'From network',
},
to: {
toFormInput: {
heading: 'To',
selectDefaultText: 'Select wallet and network',
walletSelectorHeading: 'To wallet',
Expand Down
274 changes: 170 additions & 104 deletions packages/checkout/widgets-lib/src/widgets/x-bridge/XBridgeWidget.cy.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
initialXBridgeState,
} from './context/XBridgeContext';
import { widgetTheme } from '../../lib/theme';
import { BridgeWalletSelection } from './views/BridgeWalletSelection';
import { WalletNetworkSelectionView } from './views/WalletNetworkSelectionView';
import { Bridge } from './views/Bridge';

export type BridgeWidgetInputs = BridgeWidgetParams & {
Expand All @@ -39,7 +39,11 @@ export function XBridgeWidget({

const [viewState, viewDispatch] = useReducer(
viewReducer,
{ ...initialViewState, view: { type: XBridgeWidgetViews.BRIDGE_WALLET_SELECTION } },
{
...initialViewState,
view: { type: XBridgeWidgetViews.WALLET_NETWORK_SECLECTION },
history: [{ type: XBridgeWidgetViews.WALLET_NETWORK_SECLECTION }],
},
);
const [bridgeState, bridgeDispatch] = useReducer(
xBridgeReducer,
Expand All @@ -59,8 +63,8 @@ export function XBridgeWidget({
<ViewContext.Provider value={viewReducerValues}>
<XBridgeContext.Provider value={bridgeReducerValues}>
<CryptoFiatProvider environment={environment}>
{viewState.view.type === XBridgeWidgetViews.BRIDGE_WALLET_SELECTION && (
<BridgeWalletSelection />
{viewState.view.type === XBridgeWidgetViews.WALLET_NETWORK_SECLECTION && (
<WalletNetworkSelectionView />
)}
{viewState.view.type === XBridgeWidgetViews.BRIDGE_FORM && (
<Bridge />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChainId, ChainName } from '@imtbl/checkout-sdk';
import { MenuItem } from '@biom3/react';
import { networkItemStyles } from './BridgeNetworkItemStyles';
import { networkItemStyles } from './NetworkItemStyles';

const networkIcon = {
[ChainId.IMTBL_ZKEVM_DEVNET]: 'Immutable',
Expand All @@ -15,7 +15,7 @@ export interface BridgeNetworkProps {
chainName: ChainName
onNetworkClick: (chainId: ChainId) => Promise<void>;
}
export function BridgeNetworkItem({
export function NetworkItem({
testId,
chainId,
chainName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import { text } from 'resources/text/textConfig';
import { XBridgeWidgetViews } from 'context/view-context/XBridgeViewContextTypes';
import {
useCallback, useContext, useMemo, useRef, useState,
useCallback, useContext, useEffect, useMemo, useRef, useState,
} from 'react';
import {
WalletProviderName,
Expand All @@ -18,19 +18,26 @@ import { Web3Provider } from '@ethersproject/providers';
import { isMetaMaskProvider, isPassportProvider } from 'lib/providerUtils';
import { getL1ChainId, getL2ChainId } from 'lib';
import { getChainNameById } from 'lib/chainName';
import { bridgeHeadingStyles, brigdeWalletWrapperStyles } from './BridgeWalletFormStyles';
import { XBridgeContext } from '../context/XBridgeContext';
import { BridgeNetworkItem } from './BridgeNetworkItem';
import { ViewActions, ViewContext } from 'context/view-context/ViewContext';
import { abbreviateAddress } from 'lib/addressUtils';
import {
bridgeHeadingStyles,
brigdeWalletWrapperStyles,
submitButtonWrapperStyles,
} from './WalletAndNetworkSelectorStyles';
import { BridgeActions, XBridgeContext } from '../context/XBridgeContext';
import { NetworkItem } from './NetworkItem';
import { WalletNetworkButton } from './WalletNetworkButton';
import { WalletSelector } from './WalletSelector';
import { WalletDrawer } from './WalletDrawer';

const testId = 'bridge-wallet-form';
const testId = 'wallet-network-selector';

export function BridgeWalletForm() {
const { bridgeState: { checkout } } = useContext(XBridgeContext);
export function WalletAndNetworkSelector() {
const { bridgeState: { checkout, from, to }, bridgeDispatch } = useContext(XBridgeContext);
const { viewDispatch } = useContext(ViewContext);
const {
heading, from, to, submitButton,
} = text.views[XBridgeWidgetViews.BRIDGE_WALLET_SELECTION];
heading, fromFormInput, toFormInput, submitButton,
} = text.views[XBridgeWidgetViews.WALLET_NETWORK_SECLECTION];

// calculating l1/l2 chains to work with based on Checkout environment
const l1NetworkChainId = getL1ChainId(checkout.config);
Expand All @@ -46,11 +53,13 @@ export function BridgeWalletForm() {
const [fromNetworkDrawerOpen, setFromNetworkDrawerOpen] = useState(false);
const [fromWalletWeb3Provider, setFromWalletWeb3Provider] = useState<Web3Provider | null>();
const [fromNetwork, setFromNetwork] = useState<ChainId | null>();
const [fromWalletAddress, setFromWalletAddress] = useState<string>('');

/** To wallet local state */
const [toWalletDrawerOpen, setToWalletDrawerOpen] = useState(false);
const [toWalletWeb3Provider, setToWalletWeb3Provider] = useState<Web3Provider | null>();
const [toNetwork, setToNetwork] = useState<ChainId | null>();
const [toWalletAddress, setToWalletAddress] = useState<string>('');

/* Derived state */
const isFromWalletAndNetworkSelected = fromWalletWeb3Provider && fromNetwork;
Expand Down Expand Up @@ -89,6 +98,19 @@ export function BridgeWalletForm() {
return options;
}, [checkout, fromNetwork, fromWalletProviderName]);

useEffect(() => {
if (!from || !to) return;

// add local state from context values
// if user has clicked back button
setFromWalletWeb3Provider(from.web3Provider);
setFromWalletAddress(from.walletAddress);
setFromNetwork(from.network);
setToWalletWeb3Provider(to.web3Provider);
setToWalletAddress(to.walletAddress);
setToNetwork(to.network);
}, [from, to]);

async function createProviderAndConnect(walletProviderName: WalletProviderName): Promise<Web3Provider | undefined> {
let provider;
try {
Expand Down Expand Up @@ -155,6 +177,8 @@ export function BridgeWalletForm() {
}

setFromWalletWeb3Provider(provider);
const address = await provider!.getSigner().getAddress();
setFromWalletAddress(address);

/** if Passport skip from network selector and default to zkEVM */
if (isPassportProvider(provider)) {
Expand Down Expand Up @@ -213,13 +237,21 @@ export function BridgeWalletForm() {
console.error(err);
}
},
[fromWalletWeb3Provider, fromWalletProviderName, providerCache.current],
[
checkout,
fromWalletWeb3Provider,
fromWalletProviderName,
providerCache.current,
fromNetwork,
],
);

const handleToWalletSelection = useCallback(async (selectedToWalletProviderName: WalletProviderName) => {
if (fromWalletProviderName === selectedToWalletProviderName) {
// if same from wallet and to wallet, just use the existing fromWalletLocalWeb3Provider
setToWalletWeb3Provider(fromWalletWeb3Provider);
const address = await fromWalletWeb3Provider!.getSigner().getAddress();
setToWalletAddress(address);
} else {
let toWalletProvider = providerCache.current.get(selectedToWalletProviderName);
if (!toWalletProvider) {
Expand All @@ -230,14 +262,74 @@ export function BridgeWalletForm() {
}
}
setToWalletWeb3Provider(toWalletProvider);
const address = await toWalletProvider!.getSigner().getAddress();
setToWalletAddress(address);
}

// toNetwork is always the opposite of fromNetwork
const theToNetwork = fromNetwork === l1NetworkChainId ? imtblZkEvmNetworkChainId : l1NetworkChainId;
setToNetwork(theToNetwork);

setToWalletDrawerOpen(false);
}, [fromWalletProviderName, fromNetwork]);
}, [fromWalletProviderName, fromNetwork, fromWalletWeb3Provider, providerCache.current]);

const handleSubmitDetails = useCallback(
() => {
if (!fromWalletWeb3Provider || !fromNetwork || !toWalletWeb3Provider || !toNetwork) return;

bridgeDispatch({
payload: {
type: BridgeActions.SET_PROVIDER,
web3Provider: fromWalletWeb3Provider,
},
});

bridgeDispatch({
payload: {
type: BridgeActions.SET_TOKEN_BALANCES,
tokenBalances: [],
},
});

bridgeDispatch({
payload: {
type: BridgeActions.SET_ALLOWED_TOKENS,
allowedTokens: [],
},
});

bridgeDispatch({
payload: {
type: BridgeActions.SET_WALLETS_AND_NETWORKS,
from: {
web3Provider: fromWalletWeb3Provider,
walletAddress: fromWalletAddress,
network: fromNetwork,
},
to: {
web3Provider: toWalletWeb3Provider,
walletAddress: toWalletAddress,
network: toNetwork,
},
},
});

viewDispatch({
payload: {
type: ViewActions.UPDATE_VIEW,
view: { type: XBridgeWidgetViews.BRIDGE_FORM },
},
});
},
[
fromWalletWeb3Provider,
fromNetwork,
fromWalletAddress,
toWalletWeb3Provider,
toNetwork,
toWalletAddress,
],
);

return (
<Box testId={testId} sx={brigdeWalletWrapperStyles}>
Expand All @@ -250,9 +342,9 @@ export function BridgeWalletForm() {
{heading}
</Heading>

<Heading size="xSmall" sx={{ paddingBottom: 'base.spacing.x2' }}>{from.heading}</Heading>
<Heading size="xSmall" sx={{ paddingBottom: 'base.spacing.x2' }}>{fromFormInput.heading}</Heading>
{/* Show the from wallet target (select box) if no selections have been made yet */}
<WalletSelector
<WalletDrawer
testId={testId}
type="from"
showWalletSelectorTarget={!isFromWalletAndNetworkSelected}
Expand All @@ -268,7 +360,7 @@ export function BridgeWalletForm() {
<WalletNetworkButton
testId={testId}
walletName={fromWalletProviderName}
walletAddress="0x1234...4321"
walletAddress={abbreviateAddress(fromWalletAddress)}
chainId={fromNetwork}
onWalletClick={() => {
setFromWalletDrawerOpen(true);
Expand All @@ -277,8 +369,8 @@ export function BridgeWalletForm() {
/>

<Box>
<Heading size="xSmall" sx={{ paddingBottom: 'base.spacing.x2' }}>{to.heading}</Heading>
<WalletSelector
<Heading size="xSmall" sx={{ paddingBottom: 'base.spacing.x2' }}>{toFormInput.heading}</Heading>
<WalletDrawer
testId={testId}
type="to"
showWalletSelectorTarget={!isToWalletAndNetworkSelected}
Expand All @@ -293,15 +385,15 @@ export function BridgeWalletForm() {

{/** From Network Selector, we programatically open this so there is no target */}
<BottomSheet
headerBarTitle={from.networkSelectorHeading}
headerBarTitle={fromFormInput.networkSelectorHeading}
size="full"
onCloseBottomSheet={() => {
setFromNetworkDrawerOpen(false);
}}
visible={fromNetworkDrawerOpen}
>
<BottomSheet.Content>
<BridgeNetworkItem
<NetworkItem
key={imtblZkEvmNetworkName}
testId={testId}
chainName={imtblZkEvmNetworkName}
Expand All @@ -310,7 +402,7 @@ export function BridgeWalletForm() {
/>
{/** Show L1 option for Metamask only */}
{fromWalletProviderName === WalletProviderName.METAMASK && (
<BridgeNetworkItem
<NetworkItem
key={l1NetworkName}
testId={testId}
chainName={l1NetworkName}
Expand All @@ -334,7 +426,7 @@ export function BridgeWalletForm() {
<WalletNetworkButton
testId={testId}
walletName={toWalletProviderName}
walletAddress="0x1234...4321"
walletAddress={abbreviateAddress(toWalletAddress)}
chainId={toNetwork!}
disableNetworkButton
onWalletClick={() => {
Expand All @@ -343,7 +435,15 @@ export function BridgeWalletForm() {
// eslint-disable-next-line no-console
onNetworkClick={() => {}}
/>
<Button testId={`${testId}-submit-button`} size="large">{submitButton.text}</Button>
<Box sx={submitButtonWrapperStyles}>
<Button
testId={`${testId}-submit-button`}
size="large"
onClick={handleSubmitDetails}
>
{submitButton.text}
</Button>
</Box>
</Box>
)}
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ export const bridgeHeadingStyles = {
paddingTop: 'base.spacing.x10',
paddingBottom: 'base.spacing.x4',
};

export const submitButtonWrapperStyles = {
display: 'flex',
flexDirection: 'column',
paddingY: 'base.spacing.x6',
};
Loading

0 comments on commit 6caf39e

Please sign in to comment.