- Using pool {shortenAddress(poolAddress)} holding tokens in
- this account:
+ Using pool
+
+ holding tokens in this account:
- {shortenAddress(toCustodyAddress) +
- ` (Balance: ${toCustodyBalance}${
- toMetadata.symbol && " " + toMetadata.symbol
- })`}
+
+ {` (Balance: ${toCustodyBalance}${
+ toMetadata.symbol && " " + toMetadata.symbol
+ })`}
>
) : null}
diff --git a/bridge_ui/src/components/NFT/SourcePreview.tsx b/bridge_ui/src/components/NFT/SourcePreview.tsx
index d4d4a3ea..7ac099f6 100644
--- a/bridge_ui/src/components/NFT/SourcePreview.tsx
+++ b/bridge_ui/src/components/NFT/SourcePreview.tsx
@@ -5,7 +5,7 @@ import {
selectNFTSourceParsedTokenAccount,
} from "../../store/selectors";
import { CHAINS_BY_ID } from "../../utils/consts";
-import { shortenAddress } from "../../utils/solana";
+import SmartAddress from "../SmartAddress";
import NFTViewer from "../TokenSelectors/NFTViewer";
const useStyles = makeStyles((theme) => ({
@@ -21,13 +21,24 @@ export default function SourcePreview() {
selectNFTSourceParsedTokenAccount
);
- const explainerString = sourceParsedTokenAccount
- ? `You will transfer 1 NFT of ${shortenAddress(
- sourceParsedTokenAccount?.mintKey
- )}, from ${shortenAddress(sourceParsedTokenAccount?.publicKey)} on ${
- CHAINS_BY_ID[sourceChain].name
- }`
- : "Step complete.";
+ const explainerContent =
+ sourceChain && sourceParsedTokenAccount ? (
+ <>
+ You will transfer 1 NFT of
+
+ from
+
+ on {CHAINS_BY_ID[sourceChain].name}
+ >
+ ) : (
+ ""
+ );
return (
<>
@@ -36,7 +47,7 @@ export default function SourcePreview() {
variant="subtitle2"
className={classes.description}
>
- {explainerString}
+ {explainerContent}
{sourceParsedTokenAccount ? (
diff --git a/bridge_ui/src/components/NFT/TargetPreview.tsx b/bridge_ui/src/components/NFT/TargetPreview.tsx
index fd665e5d..ccb04943 100644
--- a/bridge_ui/src/components/NFT/TargetPreview.tsx
+++ b/bridge_ui/src/components/NFT/TargetPreview.tsx
@@ -6,7 +6,7 @@ import {
} from "../../store/selectors";
import { hexToNativeString } from "../../utils/array";
import { CHAINS_BY_ID } from "../../utils/consts";
-import { shortenAddress } from "../../utils/solana";
+import SmartAddress from "../SmartAddress";
const useStyles = makeStyles((theme) => ({
description: {
@@ -20,11 +20,16 @@ export default function TargetPreview() {
const targetAddress = useSelector(selectNFTTargetAddressHex);
const targetAddressNative = hexToNativeString(targetAddress, targetChain);
- const explainerString = targetAddressNative
- ? `to ${shortenAddress(targetAddressNative)} on ${
- CHAINS_BY_ID[targetChain].name
- }`
- : "Step complete.";
+ const explainerContent =
+ targetChain && targetAddressNative ? (
+ <>
+ to
+
+ on {CHAINS_BY_ID[targetChain].name}
+ >
+ ) : (
+ ""
+ );
return (
- {explainerString}
+ {explainerContent}
);
}
diff --git a/bridge_ui/src/components/SmartAddress.tsx b/bridge_ui/src/components/SmartAddress.tsx
new file mode 100644
index 00000000..23536d01
--- /dev/null
+++ b/bridge_ui/src/components/SmartAddress.tsx
@@ -0,0 +1,159 @@
+import {
+ ChainId,
+ CHAIN_ID_ETH,
+ CHAIN_ID_SOLANA,
+} from "@certusone/wormhole-sdk";
+import { Button, makeStyles, Tooltip, Typography } from "@material-ui/core";
+import { withStyles } from "@material-ui/styles";
+import { useSnackbar } from "notistack";
+import { useCallback } from "react";
+import { ParsedTokenAccount } from "../store/transferSlice";
+import { CLUSTER } from "../utils/consts";
+import { shortenAddress } from "../utils/solana";
+import { FileCopy, OpenInNew } from "@material-ui/icons";
+
+const useStyles = makeStyles((theme) => ({
+ mainTypog: {
+ display: "inline-block",
+ marginLeft: theme.spacing(1),
+ marginRight: theme.spacing(1),
+ textDecoration: "underline",
+ textUnderlineOffset: "2px",
+ },
+ buttons: {
+ marginLeft: ".5rem",
+ marginRight: ".5rem",
+ },
+}));
+
+function pushToClipboard(content: any) {
+ if (!navigator.clipboard) {
+ // Clipboard API not available
+ return;
+ }
+ return navigator.clipboard.writeText(content);
+}
+
+const tooltipStyles = {
+ tooltip: {
+ minWidth: "max-content",
+ textAlign: "center",
+ "& > *": {
+ margin: ".25rem",
+ },
+ },
+};
+
+// @ts-ignore
+const StyledTooltip = withStyles(tooltipStyles)(Tooltip);
+
+export default function SmartAddress({
+ chainId,
+ parsedTokenAccount,
+ address,
+ symbol,
+ tokenName,
+ variant,
+}: {
+ chainId: ChainId;
+ parsedTokenAccount?: ParsedTokenAccount;
+ address?: string;
+ logo?: string;
+ tokenName?: string;
+ symbol?: string;
+ variant?: any;
+}) {
+ const classes = useStyles();
+ const useableAddress = parsedTokenAccount?.mintKey || address || "";
+ const useableSymbol = parsedTokenAccount?.symbol || symbol || "";
+ const isNative = parsedTokenAccount?.isNativeAsset || false;
+ const addressShort = shortenAddress(useableAddress) || "";
+ const { enqueueSnackbar } = useSnackbar();
+
+ const useableName = isNative
+ ? "Native Currency"
+ : parsedTokenAccount?.name
+ ? parsedTokenAccount.name
+ : tokenName
+ ? tokenName
+ : "";
+ //TODO terra
+ const explorerAddress = isNative
+ ? null
+ : chainId === CHAIN_ID_ETH
+ ? `https://${
+ CLUSTER === "testnet" ? "goerli." : ""
+ }etherscan.io/address/${useableAddress}`
+ : chainId === CHAIN_ID_SOLANA
+ ? `https://explorer.solana.com/address/${useableAddress}${
+ CLUSTER === "testnet"
+ ? "?cluster=testnet"
+ : CLUSTER === "devnet"
+ ? "?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899"
+ : ""
+ }`
+ : undefined;
+ const explorerName = chainId === CHAIN_ID_ETH ? "Etherscan" : "Explorer";
+
+ const copyToClipboard = useCallback(() => {
+ pushToClipboard(useableAddress)?.then(() => {
+ enqueueSnackbar("Copied address to clipboard.", { variant: "success" });
+ });
+ }, [useableAddress, enqueueSnackbar]);
+
+ const explorerButton = !explorerAddress ? null : (
+ }
+ className={classes.buttons}
+ href={explorerAddress}
+ target="_blank"
+ >
+ {"View on " + explorerName}
+
+ );
+ //TODO add icon here
+ const copyButton = isNative ? null : (
+ }
+ onClick={copyToClipboard}
+ className={classes.buttons}
+ >
+ Copy
+
+ );
+
+ const tooltipContent = (
+ <>
+ {useableName && {useableName}}
+ {useableSymbol && !isNative && (
+
+ {addressShort}
+
+ )}
+
+ {explorerButton}
+ {copyButton}
+
+ >
+ );
+
+ return (
+
+
+ {useableSymbol || addressShort}
+
+
+ );
+}
diff --git a/bridge_ui/src/components/Transfer/SourcePreview.tsx b/bridge_ui/src/components/Transfer/SourcePreview.tsx
index 598a6ccc..1bd17bb3 100644
--- a/bridge_ui/src/components/Transfer/SourcePreview.tsx
+++ b/bridge_ui/src/components/Transfer/SourcePreview.tsx
@@ -6,7 +6,7 @@ import {
selectTransferSourceParsedTokenAccount,
} from "../../store/selectors";
import { CHAINS_BY_ID } from "../../utils/consts";
-import { shortenAddress } from "../../utils/solana";
+import SmartAddress from "../SmartAddress";
import TokenBlacklistWarning from "./TokenBlacklistWarning";
const useStyles = makeStyles((theme) => ({
@@ -23,22 +23,19 @@ export default function SourcePreview() {
);
const sourceAmount = useSelector(selectTransferAmount);
- const plural = parseInt(sourceAmount) !== 1;
-
- const tokenExplainer = !sourceParsedTokenAccount
- ? ""
- : sourceParsedTokenAccount.isNativeAsset
- ? sourceParsedTokenAccount.symbol
- : `token${plural ? "s" : ""} of ${
- sourceParsedTokenAccount.symbol ||
- shortenAddress(sourceParsedTokenAccount.mintKey)
- }`;
-
- const explainerString = sourceParsedTokenAccount
- ? `You will transfer ${sourceAmount} ${tokenExplainer}, from ${shortenAddress(
- sourceParsedTokenAccount?.publicKey
- )} on ${CHAINS_BY_ID[sourceChain].name}`
- : "";
+ const explainerContent =
+ sourceChain && sourceParsedTokenAccount ? (
+ <>
+ You will transfer {sourceAmount}
+
+ from {CHAINS_BY_ID[sourceChain].name}
+ >
+ ) : (
+ ""
+ );
return (
<>
@@ -47,7 +44,7 @@ export default function SourcePreview() {
variant="subtitle2"
className={classes.description}
>
- {explainerString}
+ {explainerContent}
({
description: {
@@ -20,11 +20,16 @@ export default function TargetPreview() {
const targetAddress = useSelector(selectTransferTargetAddressHex);
const targetAddressNative = hexToNativeString(targetAddress, targetChain);
- const explainerString = targetAddressNative
- ? `to ${shortenAddress(targetAddressNative)} on ${
- CHAINS_BY_ID[targetChain].name
- }`
- : "Step complete.";
+ const explainerContent =
+ targetChain && targetAddressNative ? (
+ <>
+ to
+
+ on {CHAINS_BY_ID[targetChain].name}
+ >
+ ) : (
+ ""
+ );
return (
- {explainerString}
+ {explainerContent}
);
}
diff --git a/bridge_ui/src/utils/consts.ts b/bridge_ui/src/utils/consts.ts
index 4dc5cc9c..ac3e811a 100644
--- a/bridge_ui/src/utils/consts.ts
+++ b/bridge_ui/src/utils/consts.ts
@@ -317,7 +317,7 @@ export const MIGRATION_ASSET_MAP = new Map(
: [
// [
// "2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ",
- // "ApgUoB1467PXXofoLWFELH2Kz9DKB8WXdU2szGSsFKhX",
+ // "GcdupcwxkmVGM6s9F8bHSjNoznXAb3hRJTioABNYkn31",
// ],
]
);