bridge_ui: smart addresses
Change-Id: I3039c7988f7571df4785df218f93ac54dd5ef427
This commit is contained in:
parent
01d84dc7ba
commit
6ab6c95fdf
|
@ -5,7 +5,7 @@ import {
|
|||
selectAttestSourceChain,
|
||||
} from "../../store/selectors";
|
||||
import { CHAINS_BY_ID } from "../../utils/consts";
|
||||
import { shortenAddress } from "../../utils/solana";
|
||||
import SmartAddress from "../SmartAddress";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
description: {
|
||||
|
@ -18,11 +18,16 @@ export default function SourcePreview() {
|
|||
const sourceChain = useSelector(selectAttestSourceChain);
|
||||
const sourceAsset = useSelector(selectAttestSourceAsset);
|
||||
|
||||
const explainerString = sourceAsset
|
||||
? `You will attest ${shortenAddress(sourceAsset)} on ${
|
||||
CHAINS_BY_ID[sourceChain].name
|
||||
}`
|
||||
: "Step complete.";
|
||||
const explainerContent =
|
||||
sourceChain && sourceAsset ? (
|
||||
<>
|
||||
<span>You will attest</span>
|
||||
<SmartAddress chainId={sourceChain} address={sourceAsset} />
|
||||
<span>on {CHAINS_BY_ID[sourceChain].name}</span>
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
|
||||
return (
|
||||
<Typography
|
||||
|
@ -30,7 +35,7 @@ export default function SourcePreview() {
|
|||
variant="subtitle2"
|
||||
className={classes.description}
|
||||
>
|
||||
{explainerString}
|
||||
{explainerContent}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,14 +23,11 @@ import useIsWalletReady from "../../hooks/useIsWalletReady";
|
|||
import useMetaplexData from "../../hooks/useMetaplexData";
|
||||
import useSolanaTokenMap from "../../hooks/useSolanaTokenMap";
|
||||
import { MIGRATION_PROGRAM_ADDRESS, SOLANA_HOST } from "../../utils/consts";
|
||||
import {
|
||||
getMultipleAccounts,
|
||||
shortenAddress,
|
||||
signSendAndConfirm,
|
||||
} from "../../utils/solana";
|
||||
import { getMultipleAccounts, signSendAndConfirm } from "../../utils/solana";
|
||||
import ButtonWithLoader from "../ButtonWithLoader";
|
||||
import LowBalanceWarning from "../LowBalanceWarning";
|
||||
import ShowTx from "../ShowTx";
|
||||
import SmartAddress from "../SmartAddress";
|
||||
import SolanaCreateAssociatedAddress, {
|
||||
useAssociatedAccountExistsState,
|
||||
} from "../SolanaCreateAssociatedAddress";
|
||||
|
@ -41,7 +38,7 @@ const useStyles = makeStyles(() => ({
|
|||
textAlign: "center",
|
||||
padding: "2rem",
|
||||
"& > h, p ": {
|
||||
margin: "1rem",
|
||||
margin: ".5rem",
|
||||
},
|
||||
},
|
||||
divider: {
|
||||
|
@ -386,12 +383,22 @@ export default function Workflow({
|
|||
const toMetadata = getMetadata(toMint);
|
||||
const fromMetadata = getMetadata(fromMint);
|
||||
|
||||
const toMintPrettyString = toMetadata.symbol
|
||||
? toMetadata.symbol + " (" + shortenAddress(toMint) + ")"
|
||||
: shortenAddress(toMint);
|
||||
const fromMintPrettyString = fromMetadata.symbol
|
||||
? fromMetadata.symbol + " (" + shortenAddress(fromMint) + ")"
|
||||
: shortenAddress(fromMint);
|
||||
const toMintPretty = (
|
||||
<SmartAddress
|
||||
chainId={CHAIN_ID_SOLANA}
|
||||
address={toMint}
|
||||
symbol={toMetadata?.symbol}
|
||||
tokenName={toMetadata?.name}
|
||||
/>
|
||||
);
|
||||
const fromMintPretty = (
|
||||
<SmartAddress
|
||||
chainId={CHAIN_ID_SOLANA}
|
||||
address={fromMint}
|
||||
symbol={fromMetadata?.symbol}
|
||||
tokenName={fromMetadata?.name}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Container maxWidth="md">
|
||||
|
@ -407,28 +414,40 @@ export default function Workflow({
|
|||
{fromTokenAccount && toTokenAccount && fromTokenAccountBalance ? (
|
||||
<>
|
||||
<Typography variant="body2">
|
||||
This will migrate {fromMintPrettyString} tokens in this account:
|
||||
<span>This will migrate</span>
|
||||
{fromMintPretty}
|
||||
<span>tokens in this account:</span>
|
||||
</Typography>
|
||||
<Typography variant="h5">
|
||||
{shortenAddress(fromTokenAccount) +
|
||||
` (Balance: ${fromTokenAccountBalance}${
|
||||
<SmartAddress
|
||||
address={fromTokenAccount}
|
||||
chainId={CHAIN_ID_SOLANA}
|
||||
/>
|
||||
{`(Balance: ${fromTokenAccountBalance}${
|
||||
fromMetadata.symbol && " " + fromMetadata.symbol
|
||||
})`}
|
||||
</Typography>
|
||||
<div className={classes.spacer} />
|
||||
<Typography variant="body2">
|
||||
into {toMintPrettyString} tokens in this account:
|
||||
<span>into </span>
|
||||
{toMintPretty}
|
||||
<span> tokens in this account:</span>
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color={toTokenAccountExists ? "textPrimary" : "textSecondary"}
|
||||
>
|
||||
{shortenAddress(toTokenAccount) +
|
||||
(toTokenAccountExists
|
||||
<SmartAddress
|
||||
address={toTokenAccount}
|
||||
chainId={CHAIN_ID_SOLANA}
|
||||
/>
|
||||
<span>
|
||||
{toTokenAccountExists
|
||||
? ` (Balance: ${toTokenAccountBalance}${
|
||||
(toMetadata.symbol && " " + toMetadata.symbol) || ""
|
||||
})`
|
||||
: " (Not created yet)")}
|
||||
: " (Not created yet)"}
|
||||
</span>
|
||||
</Typography>
|
||||
<SolanaCreateAssociatedAddress
|
||||
mintAddress={toMint}
|
||||
|
@ -440,14 +459,21 @@ export default function Workflow({
|
|||
<>
|
||||
<div className={classes.spacer} />
|
||||
<Typography variant="body2">
|
||||
Using pool {shortenAddress(poolAddress)} holding tokens in
|
||||
this account:
|
||||
<span>Using pool </span>
|
||||
<SmartAddress
|
||||
address={poolAddress}
|
||||
chainId={CHAIN_ID_SOLANA}
|
||||
/>
|
||||
<span> holding tokens in this account:</span>
|
||||
</Typography>
|
||||
<Typography variant="h5">
|
||||
{shortenAddress(toCustodyAddress) +
|
||||
` (Balance: ${toCustodyBalance}${
|
||||
<SmartAddress
|
||||
address={toCustodyAddress}
|
||||
chainId={CHAIN_ID_SOLANA}
|
||||
/>
|
||||
<span>{` (Balance: ${toCustodyBalance}${
|
||||
toMetadata.symbol && " " + toMetadata.symbol
|
||||
})`}
|
||||
})`}</span>
|
||||
</Typography>
|
||||
</>
|
||||
) : null}
|
||||
|
|
|
@ -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 ? (
|
||||
<>
|
||||
<span>You will transfer 1 NFT of</span>
|
||||
<SmartAddress
|
||||
chainId={sourceChain}
|
||||
parsedTokenAccount={sourceParsedTokenAccount}
|
||||
/>
|
||||
<span>from</span>
|
||||
<SmartAddress
|
||||
chainId={sourceChain}
|
||||
address={sourceParsedTokenAccount?.publicKey}
|
||||
/>
|
||||
<span>on {CHAINS_BY_ID[sourceChain].name}</span>
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -36,7 +47,7 @@ export default function SourcePreview() {
|
|||
variant="subtitle2"
|
||||
className={classes.description}
|
||||
>
|
||||
{explainerString}
|
||||
{explainerContent}
|
||||
</Typography>
|
||||
{sourceParsedTokenAccount ? (
|
||||
<NFTViewer value={sourceParsedTokenAccount} />
|
||||
|
|
|
@ -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 ? (
|
||||
<>
|
||||
<span>to</span>
|
||||
<SmartAddress chainId={targetChain} address={targetAddressNative} />
|
||||
<span>on {CHAINS_BY_ID[targetChain].name}</span>
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
|
||||
return (
|
||||
<Typography
|
||||
|
@ -32,7 +37,7 @@ export default function TargetPreview() {
|
|||
variant="subtitle2"
|
||||
className={classes.description}
|
||||
>
|
||||
{explainerString}
|
||||
{explainerContent}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 : (
|
||||
<Button
|
||||
size="small"
|
||||
variant="outlined"
|
||||
endIcon={<OpenInNew />}
|
||||
className={classes.buttons}
|
||||
href={explorerAddress}
|
||||
target="_blank"
|
||||
>
|
||||
{"View on " + explorerName}
|
||||
</Button>
|
||||
);
|
||||
//TODO add icon here
|
||||
const copyButton = isNative ? null : (
|
||||
<Button
|
||||
size="small"
|
||||
variant="outlined"
|
||||
endIcon={<FileCopy />}
|
||||
onClick={copyToClipboard}
|
||||
className={classes.buttons}
|
||||
>
|
||||
Copy
|
||||
</Button>
|
||||
);
|
||||
|
||||
const tooltipContent = (
|
||||
<>
|
||||
{useableName && <Typography>{useableName}</Typography>}
|
||||
{useableSymbol && !isNative && (
|
||||
<Typography noWrap variant="body2">
|
||||
{addressShort}
|
||||
</Typography>
|
||||
)}
|
||||
<div>
|
||||
{explorerButton}
|
||||
{copyButton}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledTooltip
|
||||
title={tooltipContent}
|
||||
interactive={true}
|
||||
className={classes.mainTypog}
|
||||
>
|
||||
<Typography
|
||||
variant={variant || "body1"}
|
||||
className={classes.mainTypog}
|
||||
component="div"
|
||||
>
|
||||
{useableSymbol || addressShort}
|
||||
</Typography>
|
||||
</StyledTooltip>
|
||||
);
|
||||
}
|
|
@ -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 ? (
|
||||
<>
|
||||
<span>You will transfer {sourceAmount}</span>
|
||||
<SmartAddress
|
||||
chainId={sourceChain}
|
||||
parsedTokenAccount={sourceParsedTokenAccount}
|
||||
/>
|
||||
<span>from {CHAINS_BY_ID[sourceChain].name}</span>
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -47,7 +44,7 @@ export default function SourcePreview() {
|
|||
variant="subtitle2"
|
||||
className={classes.description}
|
||||
>
|
||||
{explainerString}
|
||||
{explainerContent}
|
||||
</Typography>
|
||||
<TokenBlacklistWarning
|
||||
sourceChain={sourceChain}
|
||||
|
|
|
@ -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(selectTransferTargetAddressHex);
|
||||
const targetAddressNative = hexToNativeString(targetAddress, targetChain);
|
||||
|
||||
const explainerString = targetAddressNative
|
||||
? `to ${shortenAddress(targetAddressNative)} on ${
|
||||
CHAINS_BY_ID[targetChain].name
|
||||
}`
|
||||
: "Step complete.";
|
||||
const explainerContent =
|
||||
targetChain && targetAddressNative ? (
|
||||
<>
|
||||
<span>to</span>
|
||||
<SmartAddress chainId={targetChain} address={targetAddressNative} />
|
||||
<span>on {CHAINS_BY_ID[targetChain].name}</span>
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
|
||||
return (
|
||||
<Typography
|
||||
|
@ -32,7 +37,7 @@ export default function TargetPreview() {
|
|||
variant="subtitle2"
|
||||
className={classes.description}
|
||||
>
|
||||
{explainerString}
|
||||
{explainerContent}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -317,7 +317,7 @@ export const MIGRATION_ASSET_MAP = new Map<string, string>(
|
|||
: [
|
||||
// [
|
||||
// "2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ",
|
||||
// "ApgUoB1467PXXofoLWFELH2Kz9DKB8WXdU2szGSsFKhX",
|
||||
// "GcdupcwxkmVGM6s9F8bHSjNoznXAb3hRJTioABNYkn31",
|
||||
// ],
|
||||
]
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue