Skip to content

Commit

Permalink
Moving calls into routes from Bridge view to separate hooks (wormhole…
Browse files Browse the repository at this point in the history
…-foundation#2170)

---------

Signed-off-by: Emre Bogazliyanlioglu <emre@wormholelabs.xyz>
  • Loading branch information
emreboga committed Jun 5, 2024
1 parent 30659be commit dbd73ab
Show file tree
Hide file tree
Showing 20 changed files with 433 additions and 247 deletions.
125 changes: 125 additions & 0 deletions wormhole-connect/src/hooks/useComputeDestinationTokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import config from 'config';
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';

import {
setDestToken,
setSupportedDestTokens,
setAllSupportedDestTokens,
getNativeVersionOfToken,
} from 'store/transferInput';

import type { Route } from 'config/types';
import type { ChainName } from '@wormhole-foundation/wormhole-connect-sdk';

import { isPorticoRoute } from 'routes/porticoBridge/utils';
import { ETHBridge } from 'routes/porticoBridge/ethBridge';
import { wstETHBridge } from 'routes/porticoBridge/wstETHBridge';
import RouteOperator from 'routes/operator';

import { getWrappedToken } from 'utils';

type Props = {
sourceChain: ChainName | undefined;
sourceToken: string;
destChain: ChainName | undefined;
destToken: string;
route: Route | undefined;
};

export const useComputeDestinationTokens = (props: Props): void => {
const { sourceChain, destChain, sourceToken, destToken, route } = props;

const dispatch = useDispatch();

useEffect(() => {
if (!destChain) {
return;
}

let canceled = false;

const computeDestTokens = async () => {
let supported = await RouteOperator.allSupportedDestTokens(
config.tokens[sourceToken],
sourceChain,
destChain,
);
if (sourceToken) {
// If any of the tokens are native to the chain, only select those.
// This is to avoid users inadvertently receiving wrapped versions of the token.
const nativeTokens = supported.filter(
(t) => t.nativeChain === destChain,
);
if (nativeTokens.length > 0) {
supported = nativeTokens;
}
}
dispatch(setSupportedDestTokens(supported));
const allSupported = await RouteOperator.allSupportedDestTokens(
undefined,
sourceChain,
destChain,
);
dispatch(setAllSupportedDestTokens(allSupported));
if (destChain && supported.length === 1) {
if (!canceled) {
dispatch(setDestToken(supported[0].key));
}
}

// If all the supported tokens are the same token
// select the native version for applicable tokens
const symbols = supported.map((t) => t.symbol);
if (
destChain &&
symbols.every((s) => s === symbols[0]) &&
['USDC', 'tBTC'].includes(symbols[0])
) {
const key = supported.find(
(t) =>
t.symbol === symbols[0] &&
t.nativeChain === t.tokenId?.chain &&
t.nativeChain === destChain,
)?.key;
if (!canceled && key) {
dispatch(setDestToken(key));
}
}

// If the source token is supported by a Portico bridge route,
// then select the native version on the dest chain
if (
sourceToken &&
destToken === '' &&
destChain &&
(!route || isPorticoRoute(route))
) {
const tokenSymbol = config.tokens[sourceToken]?.symbol;
const porticoTokens = [
...ETHBridge.SUPPORTED_TOKENS,
...wstETHBridge.SUPPORTED_TOKENS,
];
if (porticoTokens.includes(tokenSymbol)) {
const isTokenSupported =
sourceToken && supported.some((t) => t.key === sourceToken);
let key = getNativeVersionOfToken(tokenSymbol, destChain);
if (!key) {
const wrapped = getWrappedToken(config.tokens[sourceToken]);
key = getNativeVersionOfToken(wrapped.symbol, destChain);
}
if (!canceled && key && isTokenSupported) {
dispatch(setDestToken(key));
}
}
}
};

computeDestTokens();

return () => {
canceled = true;
};
// IMPORTANT: do not include destToken in dependency array
}, [route, sourceToken, sourceChain, destChain, dispatch]);
};
92 changes: 92 additions & 0 deletions wormhole-connect/src/hooks/useComputeReceiveAmount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';

import { isPorticoRoute } from 'routes/porticoBridge/utils';
import RouteOperator from 'routes/operator';

import {
setReceiveAmount,
setFetchingReceiveAmount,
setReceiveAmountError,
} from 'store/transferInput';

import type { Route } from 'config/types';
import type { ChainName } from '@wormhole-foundation/wormhole-connect-sdk';
import type { PorticoBridgeState } from 'store/porticoBridge';

type Props = {
sourceChain: ChainName | undefined;
sourceToken: string;
destChain: ChainName | undefined;
destToken: string;
route: Route | undefined;
amount: string;
portico: PorticoBridgeState;
toNativeToken: number;
relayerFee: number | undefined;
};

export const useComputeReceiveAmount = (props: Props): void => {
const {
sourceChain,
destChain,
sourceToken,
destToken,
amount,
portico,
route,
toNativeToken,
relayerFee,
} = props;

const dispatch = useDispatch();

useEffect(() => {
if (
!route ||
!amount ||
!sourceToken ||
!destToken ||
!sourceChain ||
!destChain
) {
return;
}

const recomputeReceive = async () => {
try {
const routeOptions = isPorticoRoute(route)
? portico
: { toNativeToken, relayerFee };

dispatch(setFetchingReceiveAmount());

const newReceiveAmount = await RouteOperator.computeReceiveAmount(
route,
Number.parseFloat(amount),
sourceToken,
destToken,
sourceChain,
destChain,
routeOptions,
);
dispatch(setReceiveAmount(newReceiveAmount.toString()));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
dispatch(setReceiveAmountError(e.message));
}
};
recomputeReceive();
}, [
amount,
toNativeToken,
relayerFee,
route,
sourceToken,
destToken,
destChain,
sourceChain,
portico,
dispatch,
]);
};
46 changes: 46 additions & 0 deletions wormhole-connect/src/hooks/useComputeReceiverNativeBalance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import config from 'config';
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { BigNumber } from 'ethers';
import { setReceiverNativeBalance } from 'store/transferInput';

import type { WalletData } from 'store/wallet';
import type { ChainName } from '@wormhole-foundation/wormhole-connect-sdk';

import { getTokenDecimals } from 'utils';
import { toChainId } from 'utils/sdk';
import { toDecimals } from 'utils/balance';

type Props = {
sourceChain: ChainName | undefined;
destChain: ChainName | undefined;
receiving: WalletData;
};

export const useComputeReceiverNativeBalance = (props: Props): void => {
const { sourceChain, destChain, receiving } = props;
const dispatch = useDispatch();
// check destination native balance
useEffect(() => {
if (!sourceChain || !destChain || !receiving.address) {
return;
}

const chainConfig = config.chains?.[destChain];

config.wh
.getNativeBalance(receiving.address, destChain)
.then((res: BigNumber) => {
const tokenConfig = chainConfig?.gasToken
? config.tokens[chainConfig.gasToken]
: undefined;
if (!tokenConfig)
throw new Error('Could not get native gas token config');
const decimals = getTokenDecimals(
toChainId(tokenConfig.nativeChain),
'native',
);
dispatch(setReceiverNativeBalance(toDecimals(res, decimals, 6)));
});
}, [sourceChain, destChain, receiving.address, dispatch]);
};
58 changes: 58 additions & 0 deletions wormhole-connect/src/hooks/useComputeSourceTokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import config from 'config';
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';

import { setToken, setSupportedSourceTokens } from 'store/transferInput';

import type { Route } from 'config/types';
import type { ChainName } from '@wormhole-foundation/wormhole-connect-sdk';

import RouteOperator from 'routes/operator';

type Props = {
sourceChain: ChainName | undefined;
sourceToken: string;
destChain: ChainName | undefined;
destToken: string;
route: Route | undefined;
};

export const useComputeSourceTokens = (props: Props): void => {
const { sourceChain, destChain, sourceToken, destToken, route } = props;

const dispatch = useDispatch();

useEffect(() => {
if (!sourceChain) {
return;
}

let active = true;

const computeSrcTokens = async () => {
const supported = await RouteOperator.allSupportedSourceTokens(
config.tokens[destToken],
sourceChain,
destChain,
);
if (active) {
dispatch(setSupportedSourceTokens(supported));
const isTokenSupported =
sourceToken && supported.some((t) => t.key === sourceToken);
if (!isTokenSupported) {
dispatch(setToken(''));
}
if (supported.length === 1 && sourceToken === '') {
dispatch(setToken(supported[0].key));
}
}
};

computeSrcTokens();

return () => {
active = false;
};
// IMPORTANT: do not include token in dependency array
}, [route, sourceChain, destToken, dispatch]);
};
43 changes: 43 additions & 0 deletions wormhole-connect/src/hooks/useGasSlider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import config from 'config';
import RouteOperator from 'routes/operator';
import { getWrappedToken } from 'utils';

import type { ChainName } from '@wormhole-foundation/wormhole-connect-sdk';
import type { Route } from 'config/types';

type Props = {
destChain: ChainName | undefined;
destToken: string;
route: Route | undefined;
valid: boolean;
isTransactionInProgress: boolean;
};

export const useGasSlider = (
props: Props,
): {
disabled: boolean;
showGasSlider: boolean | undefined;
} => {
const { destChain, destToken, route, isTransactionInProgress, valid } = props;

const disabled = !valid || isTransactionInProgress;
const toChainConfig = destChain ? config.chains[destChain] : undefined;
const gasTokenConfig = toChainConfig
? config.tokens[toChainConfig.gasToken]
: undefined;
const wrappedGasTokenConfig = gasTokenConfig
? getWrappedToken(gasTokenConfig)
: undefined;
const willReceiveGasToken =
wrappedGasTokenConfig && destToken === wrappedGasTokenConfig.key;
const showGasSlider =
route &&
RouteOperator.getRoute(route).NATIVE_GAS_DROPOFF_SUPPORTED &&
!willReceiveGasToken;

return {
disabled,
showGasSlider,
};
};
2 changes: 1 addition & 1 deletion wormhole-connect/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ export const removeDust = (amount: BigNumber, decimals: number): BigNumber => {
* isEmptyObject({ 'a': 1 })
* // => false
*/
export const isEmptyObject = (value: Object | null | undefined) => {
export const isEmptyObject = (value: object | null | undefined) => {
if (value === null || value === undefined) {
return true;
}
Expand Down
Loading

0 comments on commit dbd73ab

Please sign in to comment.