Skip to content

Commit

Permalink
bridge_ui: success message
Browse files Browse the repository at this point in the history
fixes wormhole-foundation#374

Change-Id: Idb255da1e08fccfe3c79092ed552998178d71c02
  • Loading branch information
evan-gray committed Sep 2, 2021
1 parent 0b517e9 commit 14e891a
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 65 deletions.
4 changes: 2 additions & 2 deletions bridge_ui/src/components/ButtonWithLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ export default function ButtonWithLoader({
error,
children,
}: {
disabled: boolean;
disabled?: boolean;
onClick: () => void;
showLoader: boolean;
showLoader?: boolean;
error?: string;
children: ReactChild;
}) {
Expand Down
59 changes: 59 additions & 0 deletions bridge_ui/src/components/ShowTx.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import { Button, makeStyles, Typography } from "@material-ui/core";
import { Transaction } from "../store/transferSlice";
import { CLUSTER } from "../utils/consts";

const useStyles = makeStyles((theme) => ({
tx: {
marginTop: theme.spacing(1),
textAlign: "center",
},
viewButton: {
marginTop: theme.spacing(1),
},
}));

export default function ShowTx({
chainId,
tx,
}: {
chainId: ChainId;
tx: Transaction;
}) {
const classes = useStyles();
const showExplorerLink = CLUSTER === "testnet" || CLUSTER === "mainnet";
const explorerAddress =
chainId === CHAIN_ID_ETH
? `https://${CLUSTER === "testnet" ? "goerli." : ""}etherscan.io/tx/${
tx?.id
}`
: chainId === CHAIN_ID_SOLANA
? `https://explorer.solana.com/tx/${tx?.id}${
CLUSTER === "testnet" ? "?cluster=testnet" : ""
}`
: undefined;
const explorerName = chainId === CHAIN_ID_ETH ? "Etherscan" : "Explorer";

return (
<div className={classes.tx}>
<Typography component="div" variant="body2">
{tx.id}
</Typography>
{showExplorerLink && explorerAddress ? (
<Button
href={explorerAddress}
target="_blank"
size="small"
variant="outlined"
className={classes.viewButton}
>
View on {explorerName}
</Button>
) : null}
</div>
);
}
35 changes: 28 additions & 7 deletions bridge_ui/src/components/Transfer/RedeemPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { makeStyles, Typography } from "@material-ui/core";
import { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
selectTransferRedeemTx,
selectTransferTargetChain,
} from "../../store/selectors";
import { reset } from "../../store/transferSlice";
import ButtonWithLoader from "../ButtonWithLoader";
import ShowTx from "../ShowTx";

const useStyles = makeStyles((theme) => ({
description: {
Expand All @@ -8,17 +17,29 @@ const useStyles = makeStyles((theme) => ({

export default function RedeemPreview() {
const classes = useStyles();
const dispatch = useDispatch();
const targetChain = useSelector(selectTransferTargetChain);
const redeemTx = useSelector(selectTransferRedeemTx);
const handleResetClick = useCallback(() => {
dispatch(reset());
}, [dispatch]);

const explainerString =
"Success! The redeem transaction was submitted. The tokens will become available once the transaction confirms.";

return (
<Typography
component="div"
variant="subtitle2"
className={classes.description}
>
{explainerString}
</Typography>
<>
<Typography
component="div"
variant="subtitle2"
className={classes.description}
>
{explainerString}
</Typography>
{redeemTx ? <ShowTx chainId={targetChain} tx={redeemTx} /> : null}
<ButtonWithLoader onClick={handleResetClick}>
Transfer More Tokens!
</ButtonWithLoader>
</>
);
}
32 changes: 25 additions & 7 deletions bridge_ui/src/components/Transfer/SendPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
import { makeStyles, Typography } from "@material-ui/core";
import { useSelector } from "react-redux";
import {
selectTransferSourceChain,
selectTransferTransferTx,
} from "../../store/selectors";
import ShowTx from "../ShowTx";

const useStyles = makeStyles((theme) => ({
description: {
textAlign: "center",
},
tx: {
marginTop: theme.spacing(1),
textAlign: "center",
},
viewButton: {
marginTop: theme.spacing(1),
},
}));

export default function SendPreview() {
const classes = useStyles();
const sourceChain = useSelector(selectTransferSourceChain);
const transferTx = useSelector(selectTransferTransferTx);

const explainerString = "The tokens have been sent!";

return (
<Typography
component="div"
variant="subtitle2"
className={classes.description}
>
{explainerString}
</Typography>
<>
<Typography
component="div"
variant="subtitle2"
className={classes.description}
>
{explainerString}
</Typography>
{transferTx ? <ShowTx chainId={sourceChain} tx={transferTx} /> : null}
</>
);
}
22 changes: 15 additions & 7 deletions bridge_ui/src/components/Transfer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,21 @@ import useFetchTargetAsset from "../../hooks/useFetchTargetAsset";
import useGetBalanceEffect from "../../hooks/useGetBalanceEffect";
import {
selectTransferActiveStep,
selectTransferIsRedeemComplete,
selectTransferIsRedeeming,
selectTransferIsSendComplete,
selectTransferIsSending,
} from "../../store/selectors";
import { setStep } from "../../store/transferSlice";
import Recovery from "./Recovery";
import Redeem from "./Redeem";
import RedeemPreview from "./RedeemPreview";
import Send from "./Send";
import SendPreview from "./SendPreview";
import Source from "./Source";
import Target from "./Target";
import SourcePreview from "./SourcePreview";
import Target from "./Target";
import TargetPreview from "./TargetPreview";
import SendPreview from "./SendPreview";
import RedeemPreview from "./RedeemPreview";
// TODO: ensure that both wallets are connected to the same known network
// TODO: loaders and such, navigation block?
// TODO: refresh displayed token amount after transfer somehow, could be resolved by having different components appear
Expand All @@ -49,6 +50,7 @@ function Transfer() {
const isSending = useSelector(selectTransferIsSending);
const isSendComplete = useSelector(selectTransferIsSendComplete);
const isRedeeming = useSelector(selectTransferIsRedeeming);
const isRedeemComplete = useSelector(selectTransferIsRedeemComplete);
const preventNavigation = isSending || isSendComplete || isRedeeming;
useEffect(() => {
if (preventNavigation) {
Expand All @@ -65,7 +67,10 @@ function Transfer() {
orientation="vertical"
className={classes.rootContainer}
>
<Step expanded={activeStep >= 0} disabled={preventNavigation}>
<Step
expanded={activeStep >= 0}
disabled={preventNavigation || isRedeemComplete}
>
<StepButton onClick={() => dispatch(setStep(0))}>Source</StepButton>
<StepContent>
{activeStep === 0 ? (
Expand All @@ -75,13 +80,16 @@ function Transfer() {
)}
</StepContent>
</Step>
<Step expanded={activeStep >= 1} disabled={preventNavigation}>
<Step
expanded={activeStep >= 1}
disabled={preventNavigation || isRedeemComplete}
>
<StepButton onClick={() => dispatch(setStep(1))}>Target</StepButton>
<StepContent>
{activeStep === 1 ? <Target /> : <TargetPreview />}
</StepContent>
</Step>
<Step expanded={activeStep >= 2}>
<Step expanded={activeStep >= 2} disabled={isSendComplete}>
<StepButton onClick={() => dispatch(setStep(2))}>
Send tokens
</StepButton>
Expand All @@ -97,7 +105,7 @@ function Transfer() {
Redeem tokens
</StepButton>
<StepContent>
{activeStep === 3 ? <Redeem /> : <RedeemPreview />}
{isRedeemComplete ? <RedeemPreview /> : <Redeem />}
</StepContent>
</Step>
</Stepper>
Expand Down
6 changes: 3 additions & 3 deletions bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
ParsedTokenAccount,
receiveSourceParsedTokenAccounts,
} from "../store/transferSlice";
import { COVALENT_GET_TOKENS_URL, SOLANA_HOST } from "../utils/consts";
import { CLUSTER, COVALENT_GET_TOKENS_URL, SOLANA_HOST } from "../utils/consts";
import {
decodeMetadata,
getMetadataAddress,
Expand Down Expand Up @@ -128,8 +128,7 @@ const getEthereumAccountsCovalent = async (
}
};

const environment =
process.env.REACT_APP_CLUSTER === "testnet" ? ENV.Testnet : ENV.MainnetBeta;
const environment = CLUSTER === "testnet" ? ENV.Testnet : ENV.MainnetBeta;

const getMetaplexData = async (mintAddresses: string[]) => {
const promises = [];
Expand Down Expand Up @@ -176,6 +175,7 @@ const getSolanaParsedTokenAccounts = (
})
.then(
(result) => {
console.log(result);
const mappedItems = result.value.map((item) =>
createParsedTokenAccountFromInfo(item.pubkey, item.account)
);
Expand Down
23 changes: 16 additions & 7 deletions bridge_ui/src/hooks/useHandleRedeem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
selectTransferIsRedeeming,
selectTransferTargetChain,
} from "../store/selectors";
import { reset, setIsRedeeming } from "../store/transferSlice";
import { setIsRedeeming, setRedeemTx } from "../store/transferSlice";
import {
ETH_TOKEN_BRIDGE_ADDRESS,
SOLANA_HOST,
Expand All @@ -43,8 +43,14 @@ async function eth(
) {
dispatch(setIsRedeeming(true));
try {
await redeemOnEth(ETH_TOKEN_BRIDGE_ADDRESS, signer, signedVAA);
dispatch(reset());
const receipt = await redeemOnEth(
ETH_TOKEN_BRIDGE_ADDRESS,
signer,
signedVAA
);
dispatch(
setRedeemTx({ id: receipt.transactionHash, block: receipt.blockNumber })
);
enqueueSnackbar("Transaction confirmed", { variant: "success" });
} catch (e) {
enqueueSnackbar(parseError(e), { variant: "error" });
Expand Down Expand Up @@ -78,8 +84,9 @@ async function solana(
payerAddress,
signedVAA
);
await signSendAndConfirm(wallet, connection, transaction);
dispatch(reset());
const txid = await signSendAndConfirm(wallet, connection, transaction);
// TODO: didn't want to make an info call we didn't need, can we get the block without it by modifying the above call?
dispatch(setRedeemTx({ id: txid, block: 1 }));
enqueueSnackbar("Transaction confirmed", { variant: "success" });
} catch (e) {
enqueueSnackbar(parseError(e), { variant: "error" });
Expand All @@ -100,11 +107,13 @@ async function terra(
wallet.terraAddress,
signedVAA
);
await wallet.post({
const result = await wallet.post({
msgs: [msg],
memo: "Wormhole - Complete Transfer",
});
dispatch(reset());
dispatch(
setRedeemTx({ id: result.result.txhash, block: result.result.height })
);
enqueueSnackbar("Transaction confirmed", { variant: "success" });
} catch (e) {
enqueueSnackbar(parseError(e), { variant: "error" });
Expand Down
1 change: 0 additions & 1 deletion bridge_ui/src/hooks/useHandleTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ async function solana(
throw new Error("An error occurred while fetching the transaction info");
}
dispatch(setTransferTx({ id: txid, block: info.slot }));
enqueueSnackbar("Transaction confirmed", { variant: "success" });
const sequence = parseSequenceFromLogSolana(info);
const emitterAddress = await getEmitterAddressSolana(
SOL_TOKEN_BRIDGE_ADDRESS
Expand Down
4 changes: 2 additions & 2 deletions bridge_ui/src/hooks/useIsWalletReady.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useConnectedWallet } from "@terra-money/wallet-provider";
import { useMemo } from "react";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
import { ETH_NETWORK_CHAIN_ID } from "../utils/consts";
import { CLUSTER, ETH_NETWORK_CHAIN_ID } from "../utils/consts";

const createWalletStatus = (isReady: boolean, statusMessage: string = "") => ({
isReady,
Expand Down Expand Up @@ -45,7 +45,7 @@ function useIsWalletReady(chainId: ChainId): {
} else {
return createWalletStatus(
false,
`Wallet is not connected to ${process.env.REACT_APP_CLUSTER}. Expected Chain ID: ${ETH_NETWORK_CHAIN_ID}`
`Wallet is not connected to ${CLUSTER}. Expected Chain ID: ${ETH_NETWORK_CHAIN_ID}`
);
}
}
Expand Down
4 changes: 4 additions & 0 deletions bridge_ui/src/store/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export const selectTransferIsSending = (state: RootState) =>
state.transfer.isSending;
export const selectTransferIsRedeeming = (state: RootState) =>
state.transfer.isRedeeming;
export const selectTransferRedeemTx = (state: RootState) =>
state.transfer.redeemTx;
export const selectTransferSourceError = (
state: RootState
): string | undefined => {
Expand Down Expand Up @@ -158,6 +160,8 @@ export const selectTransferIsTargetComplete = (state: RootState) =>
!selectTransferTargetError(state);
export const selectTransferIsSendComplete = (state: RootState) =>
!!selectTransferSignedVAAHex(state);
export const selectTransferIsRedeemComplete = (state: RootState) =>
!!selectTransferRedeemTx(state);
export const selectTransferShouldLockFields = (state: RootState) =>
selectTransferIsSending(state) || selectTransferIsSendComplete(state);

Expand Down
Loading

0 comments on commit 14e891a

Please sign in to comment.