Skip to content

Commit

Permalink
feat: allow configuration of explorer links (#293)
Browse files Browse the repository at this point in the history
  • Loading branch information
DNR500 authored Sep 5, 2024
1 parent 0792c15 commit c173546
Show file tree
Hide file tree
Showing 14 changed files with 105 additions and 42 deletions.
4 changes: 4 additions & 0 deletions packages/widget-playground/src/defaultWidgetConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ export const widgetBaseConfig: WidgetConfig = {
// },
// es,
// },
// explorerUrls: {
// 42161: ['https://scan.li.fi'], // Arbitrum
// internal: ['https://jumper.exchange/scan'], // Transder ID Card
// },
};

export const defaultWidgetConfig: Partial<WidgetConfig> = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export const getConfigOutput = (
...(config.variant ? { variant: config.variant } : {}),
...(config.subvariant ? { subvariant: config.subvariant } : {}),
...(config.appearance ? { appearance: config.appearance } : {}),
...(config.explorerUrl ? { explorerUrl: config.explorerUrl } : {}),
...(theme
? {
theme: {
Expand Down
8 changes: 7 additions & 1 deletion packages/widget/src/components/Header/WalletMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useTranslation } from 'react-i18next';
import { useLocation, useNavigate } from 'react-router-dom';
import { useAccount } from '../../hooks/useAccount.js';
import { useAvailableChains } from '../../hooks/useAvailableChains.js';
import { useExplorer } from '../../hooks/useExplorer.js';
import { navigationRoutes } from '../../utils/navigationRoutes.js';
import { shortenAddress } from '../../utils/wallet.js';
import { AvatarMasked } from '../Avatar/Avatar.style.js';
Expand All @@ -34,6 +35,7 @@ export const WalletMenu = ({ onClose }: { onClose: () => void }) => {
navigate(navigationRoutes.selectWallet);
onClose();
};
const { getAddressLink } = useExplorer();

return (
<>
Expand Down Expand Up @@ -88,7 +90,11 @@ export const WalletMenu = ({ onClose }: { onClose: () => void }) => {
size="medium"
component="a"
onClick={onClose}
href={`${chain?.metamask.blockExplorerUrls[0]}address/${account.address}`}
href={
account.address
? getAddressLink(account.address, chain)
: undefined
}
target="_blank"
>
<OpenInNewRounded />
Expand Down
9 changes: 3 additions & 6 deletions packages/widget/src/components/Step/Step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Card } from '../../components/Card/Card.js';
import { CardTitle } from '../../components/Card/CardTitle.js';
import { StepActions } from '../../components/StepActions/StepActions.js';
import { Token } from '../../components/Token/Token.js';
import { useAvailableChains } from '../../hooks/useAvailableChains.js';
import { useExplorer } from '../../hooks/useExplorer.js';
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js';
import { shortenAddress } from '../../utils/wallet.js';
import { DestinationWalletAddress } from './DestinationWalletAddress.js';
Expand All @@ -21,9 +21,8 @@ export const Step: React.FC<{
toAddress?: string;
}> = ({ step, fromToken, toToken, impactToken, toAddress }) => {
const { t } = useTranslation();
const { getChainById } = useAvailableChains();
const { subvariant, subvariantOptions } = useWidgetConfig();

const { getAddressLink } = useExplorer();
const stepHasError = step.execution?.process.some(
(process) => process.status === 'FAILED',
);
Expand Down Expand Up @@ -67,9 +66,7 @@ export const Step: React.FC<{

const formattedToAddress = shortenAddress(toAddress);
const toAddressLink = toAddress
? `${
getChainById(step.action.toChainId)?.metamask.blockExplorerUrls[0]
}address/${toAddress}`
? getAddressLink(toAddress, step.action.toChainId)
: undefined;

return (
Expand Down
7 changes: 5 additions & 2 deletions packages/widget/src/components/Step/StepProcess.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { LiFiStep, Process } from '@lifi/sdk';
import { OpenInNewRounded } from '@mui/icons-material';
import { Box, Link, Typography } from '@mui/material';
import { useExplorer } from '../../hooks/useExplorer.js';
import { useProcessMessage } from '../../hooks/useProcessMessage.js';
import { CardIconButton } from '../Card/CardIconButton.js';
import { CircularProgress } from './CircularProgress.js';
Expand All @@ -10,6 +11,8 @@ export const StepProcess: React.FC<{
process: Process;
}> = ({ step, process }) => {
const { title, message } = useProcessMessage(step, process);
const { getTransactionLink } = useExplorer();

return (
<Box px={2} py={1}>
<Box
Expand All @@ -27,11 +30,11 @@ export const StepProcess: React.FC<{
>
{title}
</Typography>
{process.txLink ? (
{process.txHash ? (
<CardIconButton
size="small"
LinkComponent={Link}
href={process.txLink}
href={getTransactionLink(process.txHash, step.action.fromChainId)}
target="_blank"
rel="nofollow noreferrer"
>
Expand Down
5 changes: 4 additions & 1 deletion packages/widget/src/components/TokenList/TokenListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { MouseEventHandler } from 'react';
import { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { formatUnits } from 'viem';
import { useExplorer } from '../../hooks/useExplorer.js';
import { formatTokenAmount, formatTokenPrice } from '../../utils/format.js';
import { shortenAddress } from '../../utils/wallet.js';
import { ListItemButton } from '../ListItem/ListItemButton.js';
Expand Down Expand Up @@ -62,6 +63,8 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
isBalanceLoading,
}) => {
const { t } = useTranslation();
const { getAddressLink } = useExplorer();

const tokenPrice = token.amount
? formatTokenPrice(
formatUnits(token.amount, token.decimals),
Expand Down Expand Up @@ -130,7 +133,7 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
<IconButton
size="small"
LinkComponent={Link}
href={`${chain?.metamask.blockExplorerUrls[0]}address/${token.address}`}
href={getAddressLink(token.address, chain)}
target="_blank"
rel="nofollow noreferrer"
onClick={(e) => e.stopPropagation()}
Expand Down
51 changes: 51 additions & 0 deletions packages/widget/src/hooks/useExplorer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Chain } from '@lifi/sdk';
import { lifiExplorerUrl } from '../config/constants.js';
import { useAvailableChains } from '../hooks/useAvailableChains.js';
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js';

const sanitiseBaseUrl = (baseUrl: string) => baseUrl.trim().replace(/\/+$/, '');

export const useExplorer = () => {
const { explorerUrls } = useWidgetConfig();
const { getChainById } = useAvailableChains();

const getBaseUrl = (chain: Chain) => {
const baseUrl = explorerUrls?.[chain.id]
? explorerUrls[chain.id][0]
: chain.metamask.blockExplorerUrls[0];

return sanitiseBaseUrl(baseUrl);
};

const resolveChain = (chain: Chain | number) =>
Number.isFinite(chain) ? getChainById(chain as number) : (chain as Chain);

const getTransactionLink = (txHash: string, chain?: Chain | number) => {
if (!chain) {
const baseUrl = explorerUrls?.internal?.[0]
? sanitiseBaseUrl(explorerUrls?.internal[0])
: lifiExplorerUrl;
return `${baseUrl}/tx/${txHash}`;
}

const resolvedChain = resolveChain(chain);
return `${resolvedChain ? getBaseUrl(resolvedChain) : lifiExplorerUrl}/tx/${txHash}`;
};

const getAddressLink = (address: string, chain?: Chain | number) => {
if (!chain) {
const baseUrl = explorerUrls?.internal?.[0]
? sanitiseBaseUrl(explorerUrls?.internal[0])
: lifiExplorerUrl;
return `${baseUrl}/address/${address}`;
}

const resolvedChain = resolveChain(chain);
return `${resolvedChain ? getBaseUrl(resolvedChain) : lifiExplorerUrl}/address/${address}`;
};

return {
getTransactionLink,
getAddressLink,
};
};
2 changes: 1 addition & 1 deletion packages/widget/src/hooks/useProcessMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ export function getProcessMessage(
case LiFiErrorCode.ProviderUnavailable:
default:
title = t(`error.title.unknown`);
if (process.txLink) {
if (process.txHash) {
message = t(`error.message.transactionFailed`);
} else {
message = process.error.message || t(`error.message.unknown`);
Expand Down
10 changes: 6 additions & 4 deletions packages/widget/src/pages/SendToWallet/BookmarksPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type { BottomSheetBase } from '../../components/BottomSheet/types.js';
import { ListItemButton } from '../../components/ListItem//ListItemButton.js';
import { ListItem } from '../../components/ListItem/ListItem.js';
import { Menu } from '../../components/Menu.js';
import { useChains } from '../../hooks/useChains.js';
import { useExplorer } from '../../hooks/useExplorer.js';
import { useHeader } from '../../hooks/useHeader.js';
import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js';
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js';
Expand Down Expand Up @@ -43,11 +43,11 @@ export const BookmarksPage = () => {
const { requiredToChainType } = useToAddressRequirements();
const { addBookmark, removeBookmark, setSelectedBookmark } =
useBookmarkActions();
const { getChainById } = useChains();
const navigate = useNavigate();
const { setFieldValue } = useFieldActions();
const { setSendToWallet } = useSendToWalletActions();
const { variant } = useWidgetConfig();
const { getAddressLink } = useExplorer();

useHeader(t(`header.bookmarkedWallets`));

Expand Down Expand Up @@ -88,9 +88,11 @@ export const BookmarksPage = () => {

const handleViewOnExplorer = () => {
if (bookmark) {
const chain = getChainById(defaultChainIdsByType[bookmark.chainType]);
window.open(
`${chain?.metamask.blockExplorerUrls[0]}address/${bookmark.address}`,
getAddressLink(
bookmark.address,
defaultChainIdsByType[bookmark.chainType],
),
'_blank',
);
}
Expand Down
15 changes: 8 additions & 7 deletions packages/widget/src/pages/SendToWallet/ConnectedWalletsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ListItemButton } from '../../components/ListItem/ListItemButton.js';
import { Menu } from '../../components/Menu.js';
import type { Account } from '../../hooks/useAccount.js';
import { useAccount } from '../../hooks/useAccount.js';
import { useChains } from '../../hooks/useChains.js';
import { useExplorer } from '../../hooks/useExplorer.js';
import { useHeader } from '../../hooks/useHeader.js';
import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js';
import { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js';
Expand All @@ -33,14 +33,14 @@ export const ConnectedWalletsPage = () => {
const [selectedAccount, setSelectedAccount] = useState<Account>();
const { accounts } = useAccount();
const { setSelectedBookmark } = useBookmarkActions();
const { getChainById } = useChains();
const { requiredToChainType } = useToAddressRequirements();
const navigate = useNavigate();
const { setFieldValue } = useFieldActions();
const { setSendToWallet } = useSendToWalletActions();
const [moreMenuAnchorEl, setMenuAnchorEl] = useState<HTMLElement | null>();
const moreMenuId = useId();
const open = Boolean(moreMenuAnchorEl);
const { getAddressLink } = useExplorer();

useHeader(t('sendToWallet.connectedWallets'));

Expand Down Expand Up @@ -79,11 +79,12 @@ export const ConnectedWalletsPage = () => {

const handleViewOnExplorer = () => {
if (selectedAccount?.chainId) {
const chain = getChainById(selectedAccount.chainId);
window.open(
`${chain?.metamask.blockExplorerUrls[0]}address/${selectedAccount.address}`,
'_blank',
);
if (selectedAccount.address) {
window.open(
getAddressLink(selectedAccount.address, selectedAccount.chainId),
'_blank',
);
}
}
closeMenu();
};
Expand Down
12 changes: 6 additions & 6 deletions packages/widget/src/pages/SendToWallet/RecentWalletsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type { BottomSheetBase } from '../../components/BottomSheet/types.js';
import { ListItem } from '../../components/ListItem/ListItem.js';
import { ListItemButton } from '../../components/ListItem/ListItemButton.js';
import { Menu } from '../../components/Menu.js';
import { useChains } from '../../hooks/useChains.js';
import { useExplorer } from '../../hooks/useExplorer.js';
import { useHeader } from '../../hooks/useHeader.js';
import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js';
import type { Bookmark } from '../../stores/bookmarks/types.js';
Expand Down Expand Up @@ -47,12 +47,12 @@ export const RecentWalletsPage = () => {
setSelectedBookmark,
addRecentWallet,
} = useBookmarkActions();
const { getChainById } = useChains();
const { setFieldValue } = useFieldActions();
const { setSendToWallet } = useSendToWalletActions();
const moreMenuId = useId();
const [moreMenuAnchorEl, setMenuAnchorEl] = useState<HTMLElement | null>();
const open = Boolean(moreMenuAnchorEl);
const { getAddressLink } = useExplorer();

useHeader(t(`header.recentWallets`));

Expand Down Expand Up @@ -95,11 +95,11 @@ export const RecentWalletsPage = () => {

const handleViewOnExplorer = () => {
if (selectedRecent) {
const chain = getChainById(
defaultChainIdsByType[selectedRecent.chainType],
);
window.open(
`${chain?.metamask.blockExplorerUrls[0]}address/${selectedRecent.address}`,
getAddressLink(
selectedRecent.address,
defaultChainIdsByType[selectedRecent.chainType],
),
'_blank',
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { AccountAvatar } from '../../components/Avatar/AccountAvatar.js';
import { ListItem } from '../../components/ListItem/ListItem.js';
import { ListItemButton } from '../../components/ListItem/ListItemButton.js';
import { Menu } from '../../components/Menu.js';
import { useChains } from '../../hooks/useChains.js';
import { useExplorer } from '../../hooks/useExplorer.js';
import { useHeader } from '../../hooks/useHeader.js';
import { useNavigateBack } from '../../hooks/useNavigateBack.js';
import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js';
Expand All @@ -34,10 +34,10 @@ export const SendToConfiguredWalletPage = () => {
const { requiredToChainType } = useToAddressRequirements();
const { setSelectedBookmark } = useBookmarkActions();
const { setFieldValue } = useFieldActions();
const { getChainById } = useChains();
const moreMenuId = useId();
const [moreMenuAnchorEl, setMenuAnchorEl] = useState<HTMLElement | null>();
const open = Boolean(moreMenuAnchorEl);
const { getAddressLink } = useExplorer();

useHeader(t(`header.sendToWallet`));

Expand Down Expand Up @@ -65,11 +65,11 @@ export const SendToConfiguredWalletPage = () => {

const handleViewOnExplorer = () => {
if (selectedToAddress) {
const chain = getChainById(
defaultChainIdsByType[selectedToAddress.chainType],
);
window.open(
`${chain?.metamask.blockExplorerUrls[0]}address/${selectedToAddress.address}`,
getAddressLink(
selectedToAddress.address,
defaultChainIdsByType[selectedToAddress.chainType],
),
'_blank',
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import { useTranslation } from 'react-i18next';
import { Card } from '../../components/Card/Card.js';
import { CardIconButton } from '../../components/Card/CardIconButton.js';
import { CardTitle } from '../../components/Card/CardTitle.js';
import { lifiExplorerUrl } from '../../config/constants.js';
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js';
import { useExplorer } from '../../hooks/useExplorer.js';

interface TransferIdCardProps {
transferId: string;
Expand All @@ -18,17 +17,15 @@ const getTxHash = (transferId: string) =>

export const TransferIdCard = ({ transferId }: TransferIdCardProps) => {
const { t } = useTranslation();

const { explorerUrl } = useWidgetConfig();
const { getTransactionLink } = useExplorer();

const copyTransferId = async () => {
await navigator.clipboard.writeText(transferId);
};

const openTransferIdInExplorer = () => {
const txHash = getTxHash(transferId);
const urlBase = explorerUrl ?? lifiExplorerUrl;
window.open(`${urlBase}/tx/${txHash}`, '_blank');
window.open(getTransactionLink(txHash), '_blank');
};

return (
Expand Down
2 changes: 1 addition & 1 deletion packages/widget/src/types/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ export interface WidgetConfig {
tokens?: WidgetTokens;
languages?: WidgetLanguages;
languageResources?: LanguageResources;
explorerUrl?: string;
explorerUrls?: Record<number | 'internal', string[]>;
}

export interface WidgetConfigProps {
Expand Down

0 comments on commit c173546

Please sign in to comment.