Skip to content

Commit

Permalink
XLabs/portal-bridge-ui#1026 - block ui while the routes gets resolved…
Browse files Browse the repository at this point in the history
… to avoid bad state (#2429)
  • Loading branch information
sebastianscatularo committed Aug 26, 2024
1 parent c5cefe3 commit 6d48c49
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 23 deletions.
8 changes: 7 additions & 1 deletion wormhole-connect/src/components/AlertBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Collapse } from '@mui/material';
import React from 'react';
import { makeStyles } from 'tss-react/mui';
import AlertIcon from 'icons/Alert';
import InfoIcon from 'icons/Info';
import { OPACITY, joinClass } from 'utils/style';

const useStyles = makeStyles()((theme: any) => ({
Expand All @@ -21,13 +22,17 @@ const useStyles = makeStyles()((theme: any) => ({
warning: {
backgroundColor: theme.palette.warning[500] + OPACITY[25],
},
info: {
backgroundColor: theme.palette.info[500] + OPACITY[25],
},
}));

type Props = {
show: boolean;
content: React.ReactNode | undefined;
warning?: boolean;
error?: boolean;
info?: boolean;
margin?: string;
testId?: string;
};
Expand All @@ -42,11 +47,12 @@ function AlertBanner(props: Props) {
classes.base,
!!props.warning && classes.warning,
!!props.error && classes.error,
!!props.info && classes.info,
])}
style={{ margin: props.margin || 0 }}
data-testid={props.testId}
>
<AlertIcon />
{props.info ? <InfoIcon /> : <AlertIcon />}
{props.content}
</div>
</Collapse>
Expand Down
9 changes: 9 additions & 0 deletions wormhole-connect/src/store/transferInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export interface TransferInputState {
supportedDestTokens: TokenConfig[];
manualAddressTarget: boolean;
showManualAddressInput: boolean;
resolvingRoutes: boolean;
}

// This is a function because config might have changed since we last cleared this store
Expand Down Expand Up @@ -174,6 +175,7 @@ function getInitialState(): TransferInputState {
supportedDestTokens: [],
manualAddressTarget: false,
showManualAddressInput: config.manualTargetAddress || false,
resolvingRoutes: false,
};
}

Expand Down Expand Up @@ -297,6 +299,12 @@ export const transferInputSlice = createSlice({
name: 'transfer',
initialState: getInitialState(),
reducers: {
setResolvingRoutes(
state: TransferInputState,
{ payload }: PayloadAction<boolean>,
) {
state.resolvingRoutes = payload;
},
// validations
setValidations: (
state: TransferInputState,
Expand Down Expand Up @@ -574,6 +582,7 @@ export const {
swapChains,
setManualAddressTarget,
showManualAddressInput,
setResolvingRoutes,
} = transferInputSlice.actions;

export default transferInputSlice.reducer;
5 changes: 4 additions & 1 deletion wormhole-connect/src/utils/transferValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,9 @@ export const validateToNativeAmt = (
export const validateRoute = (
route: Route | undefined,
availableRoutes: string[] | undefined,
resolvingRoutes = false,
): ValidationErr => {
if (resolvingRoutes) return '';
if (!route || !availableRoutes || !availableRoutes.includes(route)) {
return 'No bridge or swap route available for selected tokens';
}
Expand Down Expand Up @@ -295,6 +297,7 @@ export const validateAll = async (
routeStates,
receiveAmount,
manualAddressTarget,
resolvingRoutes,
} = transferData;
const { maxSwapAmt, toNativeToken } = relayData;
const { sending, receiving } = walletData;
Expand Down Expand Up @@ -326,7 +329,7 @@ export const validateAll = async (
maxSendAmount,
isCctpTx,
),
route: validateRoute(route, availableRoutes),
route: validateRoute(route, availableRoutes, resolvingRoutes),
toNativeToken: '',
foreignAsset: validateForeignAsset(foreignAsset),
relayerFee: '',
Expand Down
9 changes: 9 additions & 0 deletions wormhole-connect/src/views/Bridge/Bridge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { isNttRoute } from 'routes/utils';
import { useConnectToLastUsedWallet } from 'utils/wallet';
import { USDTBridge } from 'routes/porticoBridge/usdtBridge';
import { isAutomatic } from 'utils/route';
import AlertBanner from 'components/AlertBanner';

const useStyles = makeStyles()((_theme) => ({
spacer: {
Expand Down Expand Up @@ -105,6 +106,7 @@ function Bridge() {
isTransactionInProgress,
amount,
manualAddressTarget,
resolvingRoutes,
}: TransferInputState = useSelector(
(state: RootState) => state.transferInput,
);
Expand Down Expand Up @@ -351,6 +353,13 @@ function Bridge() {
<SwapChains />
<ToInputs />

<AlertBanner
show={resolvingRoutes}
content="Resolving available routes..."
info
margin="0 0 16px 0"
testId="resolving-routes-message"
/>
<ValidationError
forceShow={showRouteValidation}
validations={[validations.route]}
Expand Down
51 changes: 30 additions & 21 deletions wormhole-connect/src/views/Bridge/RouteOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { RootState } from 'store';
import {
RouteState,
setManualAddressTarget,
setResolvingRoutes,
setRoutes,
setTransferRoute,
} from 'store/transferInput';
Expand Down Expand Up @@ -423,36 +424,44 @@ function RouteOptions() {
useEffect(() => {
let isActive = true;

if (!fromChain || !toChain || !token || !destToken) return;
const getAvailable = async () => {
const getAvailable = async (fromChain: ChainName, toChain: ChainName) => {
const routes: RouteState[] = [];
for (const value of config.routes) {
const r = value as Route;
const available = await RouteOperator.isRouteAvailable(
r,
token,
destToken,
debouncedAmount,
fromChain,
toChain,
);

const supported = await RouteOperator.isRouteSupported(
r,
token,
destToken,
debouncedAmount,
fromChain,
toChain,
);

// don't await, we want to resolve all routes in parallel
const [available, supported] = await Promise.all([
RouteOperator.isRouteAvailable(
r,
token,
destToken,
debouncedAmount,
fromChain,
toChain,
),
RouteOperator.isRouteSupported(
r,
token,
destToken,
debouncedAmount,
fromChain,
toChain,
),
]);
routes.push({ name: r, supported, availability: available });
}
if (isActive) {
dispatch(setRoutes(routes));
}
dispatch(setResolvingRoutes(false));
};
getAvailable();

if (!fromChain || !toChain || !token || !destToken) {
dispatch(setRoutes([])); // reset routes if we don't have all the info
} else {
dispatch(setRoutes([])); // defensive remove routes until we have all the info to decide if the routes are availables between renders
dispatch(setResolvingRoutes(true));
getAvailable(fromChain, toChain);
}

return () => {
isActive = false;
Expand Down

0 comments on commit 6d48c49

Please sign in to comment.