diff --git a/bridge_ui/src/components/Attest/Create.tsx b/bridge_ui/src/components/Attest/Create.tsx
index 70b66d4a..0fecefc2 100644
--- a/bridge_ui/src/components/Attest/Create.tsx
+++ b/bridge_ui/src/components/Attest/Create.tsx
@@ -4,6 +4,7 @@ import useIsWalletReady from "../../hooks/useIsWalletReady";
import { selectAttestTargetChain } from "../../store/selectors";
import ButtonWithLoader from "../ButtonWithLoader";
import KeyAndBalance from "../KeyAndBalance";
+import WaitingForWalletMessage from "./WaitingForWalletMessage";
function Create() {
const { handleClick, disabled, showLoader } = useHandleCreateWrapped();
@@ -20,6 +21,7 @@ function Create() {
>
Create
+
>
);
}
diff --git a/bridge_ui/src/components/Attest/CreatePreview.tsx b/bridge_ui/src/components/Attest/CreatePreview.tsx
new file mode 100644
index 00000000..1a6c8907
--- /dev/null
+++ b/bridge_ui/src/components/Attest/CreatePreview.tsx
@@ -0,0 +1,54 @@
+import { makeStyles, Typography } from "@material-ui/core";
+import { useCallback } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import {
+ selectAttestCreateTx,
+ selectAttestTargetChain,
+} from "../../store/selectors";
+import { reset } from "../../store/attestSlice";
+import ButtonWithLoader from "../ButtonWithLoader";
+import ShowTx from "../ShowTx";
+import { useHistory } from "react-router";
+
+const useStyles = makeStyles((theme) => ({
+ description: {
+ textAlign: "center",
+ },
+}));
+
+export default function CreatePreview() {
+ const { push } = useHistory();
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const targetChain = useSelector(selectAttestTargetChain);
+ const createTx = useSelector(selectAttestCreateTx);
+ const handleResetClick = useCallback(() => {
+ dispatch(reset());
+ }, [dispatch]);
+ const handleReturnClick = useCallback(() => {
+ dispatch(reset());
+ push("/transfer");
+ }, [dispatch, push]);
+
+ const explainerString =
+ "Success! The redeem transaction was submitted. The tokens will become available once the transaction confirms.";
+
+ return (
+ <>
+
+ {explainerString}
+
+ {createTx ? : null}
+
+ Attest Another Token!
+
+
+ Return to Transfer
+
+ >
+ );
+}
diff --git a/bridge_ui/src/components/Attest/Send.tsx b/bridge_ui/src/components/Attest/Send.tsx
index 270285aa..0fdae378 100644
--- a/bridge_ui/src/components/Attest/Send.tsx
+++ b/bridge_ui/src/components/Attest/Send.tsx
@@ -1,13 +1,21 @@
import { useSelector } from "react-redux";
import { useHandleAttest } from "../../hooks/useHandleAttest";
import useIsWalletReady from "../../hooks/useIsWalletReady";
-import { selectAttestSourceChain } from "../../store/selectors";
+import {
+ selectAttestAttestTx,
+ selectAttestIsSendComplete,
+ selectAttestSourceChain,
+} from "../../store/selectors";
import ButtonWithLoader from "../ButtonWithLoader";
import KeyAndBalance from "../KeyAndBalance";
+import TransactionProgress from "../TransactionProgress";
+import WaitingForWalletMessage from "./WaitingForWalletMessage";
function Send() {
const { handleClick, disabled, showLoader } = useHandleAttest();
const sourceChain = useSelector(selectAttestSourceChain);
+ const attestTx = useSelector(selectAttestAttestTx);
+ const isSendComplete = useSelector(selectAttestIsSendComplete);
const { isReady, statusMessage } = useIsWalletReady(sourceChain);
return (
@@ -21,6 +29,12 @@ function Send() {
>
Attest
+
+
>
);
}
diff --git a/bridge_ui/src/components/Attest/SendPreview.tsx b/bridge_ui/src/components/Attest/SendPreview.tsx
new file mode 100644
index 00000000..051d3f31
--- /dev/null
+++ b/bridge_ui/src/components/Attest/SendPreview.tsx
@@ -0,0 +1,41 @@
+import { makeStyles, Typography } from "@material-ui/core";
+import { useSelector } from "react-redux";
+import {
+ selectAttestSourceChain,
+ selectAttestAttestTx,
+} 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(selectAttestSourceChain);
+ const attestTx = useSelector(selectAttestAttestTx);
+
+ const explainerString = "The token has been attested!";
+
+ return (
+ <>
+
+ {explainerString}
+
+ {attestTx ? : null}
+ >
+ );
+}
diff --git a/bridge_ui/src/components/Attest/SourcePreview.tsx b/bridge_ui/src/components/Attest/SourcePreview.tsx
new file mode 100644
index 00000000..cba94a2d
--- /dev/null
+++ b/bridge_ui/src/components/Attest/SourcePreview.tsx
@@ -0,0 +1,36 @@
+import { makeStyles, Typography } from "@material-ui/core";
+import { useSelector } from "react-redux";
+import {
+ selectAttestSourceAsset,
+ selectAttestSourceChain,
+} from "../../store/selectors";
+import { CHAINS_BY_ID } from "../../utils/consts";
+import { shortenAddress } from "../../utils/solana";
+
+const useStyles = makeStyles((theme) => ({
+ description: {
+ textAlign: "center",
+ },
+}));
+
+export default function SourcePreview() {
+ const classes = useStyles();
+ const sourceChain = useSelector(selectAttestSourceChain);
+ const sourceAsset = useSelector(selectAttestSourceAsset);
+
+ const explainerString = sourceAsset
+ ? `You will attest ${shortenAddress(sourceAsset)} on ${
+ CHAINS_BY_ID[sourceChain].name
+ }`
+ : "Step complete.";
+
+ return (
+
+ {explainerString}
+
+ );
+}
diff --git a/bridge_ui/src/components/Attest/TargetPreview.tsx b/bridge_ui/src/components/Attest/TargetPreview.tsx
new file mode 100644
index 00000000..de350de5
--- /dev/null
+++ b/bridge_ui/src/components/Attest/TargetPreview.tsx
@@ -0,0 +1,27 @@
+import { makeStyles, Typography } from "@material-ui/core";
+import { useSelector } from "react-redux";
+import { selectAttestTargetChain } from "../../store/selectors";
+import { CHAINS_BY_ID } from "../../utils/consts";
+
+const useStyles = makeStyles((theme) => ({
+ description: {
+ textAlign: "center",
+ },
+}));
+
+export default function TargetPreview() {
+ const classes = useStyles();
+ const targetChain = useSelector(selectAttestTargetChain);
+
+ const explainerString = `to ${CHAINS_BY_ID[targetChain].name}`;
+
+ return (
+
+ {explainerString}
+
+ );
+}
diff --git a/bridge_ui/src/components/Attest/WaitingForWalletMessage.tsx b/bridge_ui/src/components/Attest/WaitingForWalletMessage.tsx
new file mode 100644
index 00000000..da228a60
--- /dev/null
+++ b/bridge_ui/src/components/Attest/WaitingForWalletMessage.tsx
@@ -0,0 +1,38 @@
+import { CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
+import { makeStyles, Typography } from "@material-ui/core";
+import { useSelector } from "react-redux";
+import {
+ selectAttestAttestTx,
+ selectAttestCreateTx,
+ selectAttestIsCreating,
+ selectAttestIsSending,
+ selectAttestTargetChain,
+} from "../../store/selectors";
+
+const useStyles = makeStyles((theme) => ({
+ message: {
+ color: theme.palette.warning.light,
+ marginTop: theme.spacing(1),
+ textAlign: "center",
+ },
+}));
+
+const WAITING_FOR_WALLET = "Waiting for wallet approval (likely in a popup)...";
+
+export default function WaitingForWalletMessage() {
+ const classes = useStyles();
+ const isSending = useSelector(selectAttestIsSending);
+ const attestTx = useSelector(selectAttestAttestTx);
+ const targetChain = useSelector(selectAttestTargetChain);
+ const isCreating = useSelector(selectAttestIsCreating);
+ const createTx = useSelector(selectAttestCreateTx);
+ const showWarning = (isSending && !attestTx) || (isCreating && !createTx);
+ return showWarning ? (
+
+ {WAITING_FOR_WALLET}{" "}
+ {targetChain === CHAIN_ID_SOLANA && isCreating
+ ? "Note: there will be several transactions"
+ : null}
+
+ ) : null;
+}
diff --git a/bridge_ui/src/components/Attest/index.tsx b/bridge_ui/src/components/Attest/index.tsx
index 402d5be2..5c611789 100644
--- a/bridge_ui/src/components/Attest/index.tsx
+++ b/bridge_ui/src/components/Attest/index.tsx
@@ -6,17 +6,25 @@ import {
StepContent,
Stepper,
} from "@material-ui/core";
+import { Alert } from "@material-ui/lab";
+import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
+import { setStep } from "../../store/attestSlice";
import {
selectAttestActiveStep,
- selectAttestSignedVAAHex,
+ selectAttestIsCreateComplete,
+ selectAttestIsCreating,
+ selectAttestIsSendComplete,
+ selectAttestIsSending,
} from "../../store/selectors";
-import { setStep } from "../../store/attestSlice";
import Create from "./Create";
+import CreatePreview from "./CreatePreview";
import Send from "./Send";
+import SendPreview from "./SendPreview";
import Source from "./Source";
+import SourcePreview from "./SourcePreview";
import Target from "./Target";
-import { Alert } from "@material-ui/lab";
+import TargetPreview from "./TargetPreview";
const useStyles = makeStyles(() => ({
rootContainer: {
@@ -28,7 +36,20 @@ function Attest() {
const classes = useStyles();
const dispatch = useDispatch();
const activeStep = useSelector(selectAttestActiveStep);
- const signedVAAHex = useSelector(selectAttestSignedVAAHex);
+ const isSending = useSelector(selectAttestIsSending);
+ const isSendComplete = useSelector(selectAttestIsSendComplete);
+ const isCreating = useSelector(selectAttestIsCreating);
+ const isCreateComplete = useSelector(selectAttestIsCreateComplete);
+ const preventNavigation =
+ (isSending || isSendComplete || isCreating) && !isCreateComplete;
+ useEffect(() => {
+ if (preventNavigation) {
+ window.onbeforeunload = () => true;
+ return () => {
+ window.onbeforeunload = null;
+ };
+ }
+ }, [preventNavigation]);
return (
@@ -40,39 +61,41 @@ function Attest() {
orientation="vertical"
className={classes.rootContainer}
>
-
- dispatch(setStep(0))}>
- Select a source
-
+ = 0}
+ disabled={preventNavigation || isCreateComplete}
+ >
+ dispatch(setStep(0))}>Source
-
+ {activeStep === 0 ? : }
-
- dispatch(setStep(1))}>
- Select a target
-
+ = 1}
+ disabled={preventNavigation || isCreateComplete}
+ >
+ dispatch(setStep(1))}>Target
-
+ {activeStep === 1 ? : }
-
+ = 2} disabled={isSendComplete}>
dispatch(setStep(2))}>
Send attestation
-
+ {activeStep === 2 ? : }
-
+ = 3}>
dispatch(setStep(3))}
- disabled={!signedVAAHex}
+ disabled={!isSendComplete}
>
Create wrapped token
-
+ {isCreateComplete ? : }
diff --git a/bridge_ui/src/components/NFT/Redeem.tsx b/bridge_ui/src/components/NFT/Redeem.tsx
index df5dc056..29facf6a 100644
--- a/bridge_ui/src/components/NFT/Redeem.tsx
+++ b/bridge_ui/src/components/NFT/Redeem.tsx
@@ -5,6 +5,7 @@ import { selectNFTTargetChain } from "../../store/selectors";
import ButtonWithLoader from "../ButtonWithLoader";
import KeyAndBalance from "../KeyAndBalance";
import StepDescription from "../StepDescription";
+import WaitingForWalletMessage from "./WaitingForWalletMessage";
function Redeem() {
const { handleClick, disabled, showLoader } = useHandleNFTRedeem();
@@ -22,6 +23,7 @@ function Redeem() {
>
Redeem
+
>
);
}
diff --git a/bridge_ui/src/components/NFT/Send.tsx b/bridge_ui/src/components/NFT/Send.tsx
index 1768682a..7c965896 100644
--- a/bridge_ui/src/components/NFT/Send.tsx
+++ b/bridge_ui/src/components/NFT/Send.tsx
@@ -6,12 +6,15 @@ import {
selectNFTSourceWalletAddress,
selectNFTSourceChain,
selectNFTTargetError,
+ selectNFTTransferTx,
+ selectNFTIsSendComplete,
} from "../../store/selectors";
import { CHAINS_BY_ID } from "../../utils/consts";
import ButtonWithLoader from "../ButtonWithLoader";
import KeyAndBalance from "../KeyAndBalance";
import StepDescription from "../StepDescription";
-import TransferProgress from "../TransferProgress";
+import TransactionProgress from "../TransactionProgress";
+import WaitingForWalletMessage from "./WaitingForWalletMessage";
function Send() {
const { handleClick, disabled, showLoader } = useHandleNFTTransfer();
@@ -20,6 +23,8 @@ function Send() {
const { isReady, statusMessage, walletAddress } =
useIsWalletReady(sourceChain);
const sourceWalletAddress = useSelector(selectNFTSourceWalletAddress);
+ const transferTx = useSelector(selectNFTTransferTx);
+ const isSendComplete = useSelector(selectNFTIsSendComplete);
//The chain ID compare is handled implicitly, as the isWalletReady hook should report !isReady if the wallet is on the wrong chain.
const isWrongWallet =
sourceWalletAddress &&
@@ -49,7 +54,12 @@ function Send() {
>
Transfer
-
+
+
>
);
}
diff --git a/bridge_ui/src/components/NFT/WaitingForWalletMessage.tsx b/bridge_ui/src/components/NFT/WaitingForWalletMessage.tsx
new file mode 100644
index 00000000..b3a724b7
--- /dev/null
+++ b/bridge_ui/src/components/NFT/WaitingForWalletMessage.tsx
@@ -0,0 +1,42 @@
+import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
+import { makeStyles, Typography } from "@material-ui/core";
+import { useSelector } from "react-redux";
+import {
+ selectNFTIsRedeeming,
+ selectNFTIsSending,
+ selectNFTRedeemTx,
+ selectNFTSourceChain,
+ selectNFTTargetChain,
+ selectNFTTransferTx,
+} from "../../store/selectors";
+
+const useStyles = makeStyles((theme) => ({
+ message: {
+ color: theme.palette.warning.light,
+ marginTop: theme.spacing(1),
+ textAlign: "center",
+ },
+}));
+
+const WAITING_FOR_WALLET = "Waiting for wallet approval (likely in a popup)...";
+
+export default function WaitingForWalletMessage() {
+ const classes = useStyles();
+ const sourceChain = useSelector(selectNFTSourceChain);
+ const isSending = useSelector(selectNFTIsSending);
+ const transferTx = useSelector(selectNFTTransferTx);
+ const targetChain = useSelector(selectNFTTargetChain);
+ const isRedeeming = useSelector(selectNFTIsRedeeming);
+ const redeemTx = useSelector(selectNFTRedeemTx);
+ const showWarning = (isSending && !transferTx) || (isRedeeming && !redeemTx);
+ return showWarning ? (
+
+ {WAITING_FOR_WALLET}{" "}
+ {targetChain === CHAIN_ID_SOLANA && isRedeeming
+ ? "Note: there will be several transactions"
+ : sourceChain === CHAIN_ID_ETH && isSending
+ ? "Note: there will be two transactions"
+ : null}
+
+ ) : null;
+}
diff --git a/bridge_ui/src/components/TransferProgress.tsx b/bridge_ui/src/components/TransactionProgress.tsx
similarity index 62%
rename from bridge_ui/src/components/TransferProgress.tsx
rename to bridge_ui/src/components/TransactionProgress.tsx
index 2cdb3205..fdbdd98e 100644
--- a/bridge_ui/src/components/TransferProgress.tsx
+++ b/bridge_ui/src/components/TransactionProgress.tsx
@@ -1,17 +1,13 @@
-import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
+import {
+ ChainId,
+ CHAIN_ID_ETH,
+ CHAIN_ID_SOLANA,
+} from "@certusone/wormhole-sdk";
import { LinearProgress, makeStyles, Typography } from "@material-ui/core";
import { Connection } from "@solana/web3.js";
import { useEffect, useState } from "react";
-import { useSelector } from "react-redux";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
-import {
- selectNFTIsSendComplete,
- selectNFTSourceChain,
- selectNFTTransferTx,
- selectTransferIsSendComplete,
- selectTransferSourceChain,
- selectTransferTransferTx,
-} from "../store/selectors";
+import { Transaction } from "../store/transferSlice";
import { CHAINS_BY_ID, SOLANA_HOST } from "../utils/consts";
const useStyles = makeStyles((theme) => ({
@@ -24,22 +20,21 @@ const useStyles = makeStyles((theme) => ({
},
}));
-export default function TransferProgress({ nft }: { nft?: boolean }) {
+export default function TransactionProgress({
+ chainId,
+ tx,
+ isSendComplete,
+}: {
+ chainId: ChainId;
+ tx: Transaction | undefined;
+ isSendComplete: boolean;
+}) {
const classes = useStyles();
- const sourceChain = useSelector(
- nft ? selectNFTSourceChain : selectTransferSourceChain
- );
- const transferTx = useSelector(
- nft ? selectNFTTransferTx : selectTransferTransferTx
- );
- const isSendComplete = useSelector(
- nft ? selectNFTIsSendComplete : selectTransferIsSendComplete
- );
const { provider } = useEthereumProvider();
const [currentBlock, setCurrentBlock] = useState(0);
useEffect(() => {
- if (isSendComplete || !transferTx) return;
- if (sourceChain === CHAIN_ID_ETH && provider) {
+ if (isSendComplete || !tx) return;
+ if (chainId === CHAIN_ID_ETH && provider) {
let cancelled = false;
(async () => {
while (!cancelled) {
@@ -58,7 +53,7 @@ export default function TransferProgress({ nft }: { nft?: boolean }) {
cancelled = true;
};
}
- if (sourceChain === CHAIN_ID_SOLANA) {
+ if (chainId === CHAIN_ID_SOLANA) {
let cancelled = false;
const connection = new Connection(SOLANA_HOST, "confirmed");
const sub = connection.onSlotChange((slotInfo) => {
@@ -71,20 +66,14 @@ export default function TransferProgress({ nft }: { nft?: boolean }) {
connection.removeSlotChangeListener(sub);
};
}
- }, [isSendComplete, sourceChain, provider, transferTx]);
+ }, [isSendComplete, chainId, provider, tx]);
const blockDiff =
- transferTx && transferTx.block && currentBlock
- ? currentBlock - transferTx.block
- : undefined;
+ tx && tx.block && currentBlock ? currentBlock - tx.block : undefined;
const expectedBlocks =
- sourceChain === CHAIN_ID_SOLANA
- ? 32
- : sourceChain === CHAIN_ID_ETH
- ? 15
- : 1;
+ chainId === CHAIN_ID_SOLANA ? 32 : chainId === CHAIN_ID_ETH ? 15 : 1;
if (
!isSendComplete &&
- (sourceChain === CHAIN_ID_SOLANA || sourceChain === CHAIN_ID_ETH) &&
+ (chainId === CHAIN_ID_SOLANA || chainId === CHAIN_ID_ETH) &&
blockDiff !== undefined
) {
return (
@@ -97,7 +86,7 @@ export default function TransferProgress({ nft }: { nft?: boolean }) {
/>
{blockDiff < expectedBlocks
- ? `Waiting for ${blockDiff} / ${expectedBlocks} confirmations on ${CHAINS_BY_ID[sourceChain].name}...`
+ ? `Waiting for ${blockDiff} / ${expectedBlocks} confirmations on ${CHAINS_BY_ID[chainId].name}...`
: `Waiting for Wormhole Network consensus...`}
diff --git a/bridge_ui/src/components/Transfer/Send.tsx b/bridge_ui/src/components/Transfer/Send.tsx
index 750e4f2e..4fe2d00b 100644
--- a/bridge_ui/src/components/Transfer/Send.tsx
+++ b/bridge_ui/src/components/Transfer/Send.tsx
@@ -11,16 +11,18 @@ import useIsWalletReady from "../../hooks/useIsWalletReady";
import {
selectSourceWalletAddress,
selectTransferAmount,
+ selectTransferIsSendComplete,
selectTransferSourceAsset,
selectTransferSourceChain,
selectTransferSourceParsedTokenAccount,
selectTransferTargetError,
+ selectTransferTransferTx,
} from "../../store/selectors";
import { CHAINS_BY_ID } from "../../utils/consts";
import ButtonWithLoader from "../ButtonWithLoader";
import KeyAndBalance from "../KeyAndBalance";
import StepDescription from "../StepDescription";
-import TransferProgress from "../TransferProgress";
+import TransactionProgress from "../TransactionProgress";
import WaitingForWalletMessage from "./WaitingForWalletMessage";
function Send() {
@@ -41,6 +43,8 @@ function Send() {
sourceDecimals !== undefined &&
sourceDecimals !== null &&
parseUnits("1", sourceDecimals).toBigInt();
+ const transferTx = useSelector(selectTransferTransferTx);
+ const isSendComplete = useSelector(selectTransferIsSendComplete);
const error = useSelector(selectTransferTargetError);
const [allowanceError, setAllowanceError] = useState("");
@@ -149,7 +153,11 @@ function Send() {
)}
-
+
>
);
}
diff --git a/bridge_ui/src/components/Transfer/index.tsx b/bridge_ui/src/components/Transfer/index.tsx
index e91a5694..41552f4b 100644
--- a/bridge_ui/src/components/Transfer/index.tsx
+++ b/bridge_ui/src/components/Transfer/index.tsx
@@ -51,10 +51,8 @@ function Transfer() {
(isSending || isSendComplete || isRedeeming) && !isRedeemComplete;
useEffect(() => {
if (preventNavigation) {
- console.log("add onbeforeunload");
window.onbeforeunload = () => true;
return () => {
- console.log("remove onbeforeunload");
window.onbeforeunload = null;
};
}
diff --git a/bridge_ui/src/hooks/useHandleAttest.ts b/bridge_ui/src/hooks/useHandleAttest.ts
index 557f6018..ab7325d4 100644
--- a/bridge_ui/src/hooks/useHandleAttest.ts
+++ b/bridge_ui/src/hooks/useHandleAttest.ts
@@ -24,7 +24,11 @@ import { useDispatch, useSelector } from "react-redux";
import { Signer } from "../../../sdk/js/node_modules/ethers/lib";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
-import { setIsSending, setSignedVAAHex } from "../store/attestSlice";
+import {
+ setAttestTx,
+ setIsSending,
+ setSignedVAAHex,
+} from "../store/attestSlice";
import {
selectAttestIsSendComplete,
selectAttestIsSending,
@@ -59,6 +63,9 @@ async function eth(
signer,
sourceAsset
);
+ dispatch(
+ setAttestTx({ id: receipt.transactionHash, block: receipt.blockNumber })
+ );
enqueueSnackbar("Transaction confirmed", { variant: "success" });
const sequence = parseSequenceFromLogEth(receipt, ETH_BRIDGE_ADDRESS);
const emitterAddress = getEmitterAddressEth(ETH_TOKEN_BRIDGE_ADDRESS);
@@ -101,6 +108,7 @@ async function solana(
// TODO: error state
throw new Error("An error occurred while fetching the transaction info");
}
+ dispatch(setAttestTx({ id: txid, block: info.slot }));
const sequence = parseSequenceFromLogSolana(info);
const emitterAddress = await getEmitterAddressSolana(
SOL_TOKEN_BRIDGE_ADDRESS
@@ -134,6 +142,7 @@ async function terra(
asset
);
const info = await waitForTerraExecution(result);
+ dispatch(setAttestTx({ id: info.txhash, block: info.height }));
enqueueSnackbar("Transaction confirmed", { variant: "success" });
const sequence = parseSequenceFromLogTerra(info);
if (!sequence) {
diff --git a/bridge_ui/src/hooks/useHandleCreateWrapped.ts b/bridge_ui/src/hooks/useHandleCreateWrapped.ts
index 769d8583..f135a839 100644
--- a/bridge_ui/src/hooks/useHandleCreateWrapped.ts
+++ b/bridge_ui/src/hooks/useHandleCreateWrapped.ts
@@ -20,7 +20,7 @@ import { useDispatch, useSelector } from "react-redux";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
import useAttestSignedVAA from "../hooks/useAttestSignedVAA";
-import { reset, setIsCreating } from "../store/attestSlice";
+import { setCreateTx, setIsCreating } from "../store/attestSlice";
import {
selectAttestIsCreating,
selectAttestTargetChain,
@@ -43,8 +43,14 @@ async function eth(
) {
dispatch(setIsCreating(true));
try {
- await createWrappedOnEth(ETH_TOKEN_BRIDGE_ADDRESS, signer, signedVAA);
- dispatch(reset());
+ const receipt = await createWrappedOnEth(
+ ETH_TOKEN_BRIDGE_ADDRESS,
+ signer,
+ signedVAA
+ );
+ dispatch(
+ setCreateTx({ id: receipt.transactionHash, block: receipt.blockNumber })
+ );
enqueueSnackbar("Transaction confirmed", { variant: "success" });
} catch (e) {
enqueueSnackbar(parseError(e), { variant: "error" });
@@ -76,8 +82,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(setCreateTx({ id: txid, block: 1 }));
enqueueSnackbar("Transaction confirmed", { variant: "success" });
} catch (e) {
enqueueSnackbar(parseError(e), { variant: "error" });
@@ -98,11 +105,13 @@ async function terra(
wallet.terraAddress,
signedVAA
);
- await wallet.post({
+ const result = await wallet.post({
msgs: [msg],
memo: "Wormhole - Create Wrapped",
});
- dispatch(reset());
+ dispatch(
+ setCreateTx({ id: result.result.txhash, block: result.result.height })
+ );
enqueueSnackbar("Transaction confirmed", { variant: "success" });
} catch (e) {
enqueueSnackbar(parseError(e), { variant: "error" });
diff --git a/bridge_ui/src/store/attestSlice.ts b/bridge_ui/src/store/attestSlice.ts
index 3ad31395..17d66426 100644
--- a/bridge_ui/src/store/attestSlice.ts
+++ b/bridge_ui/src/store/attestSlice.ts
@@ -4,6 +4,7 @@ import {
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
+import { Transaction } from "./transferSlice";
const LAST_STEP = 3;
@@ -14,9 +15,11 @@ export interface AttestState {
sourceChain: ChainId;
sourceAsset: string;
targetChain: ChainId;
+ attestTx: Transaction | undefined;
signedVAAHex: string | undefined;
isSending: boolean;
isCreating: boolean;
+ createTx: Transaction | undefined;
}
const initialState: AttestState = {
@@ -24,9 +27,11 @@ const initialState: AttestState = {
sourceChain: CHAIN_ID_SOLANA,
sourceAsset: "",
targetChain: CHAIN_ID_ETH,
+ attestTx: undefined,
signedVAAHex: undefined,
isSending: false,
isCreating: false,
+ createTx: undefined,
};
export const attestSlice = createSlice({
@@ -62,6 +67,9 @@ export const attestSlice = createSlice({
state.sourceAsset = "";
}
},
+ setAttestTx: (state, action: PayloadAction) => {
+ state.attestTx = action.payload;
+ },
setSignedVAAHex: (state, action: PayloadAction) => {
state.signedVAAHex = action.payload;
state.isSending = false;
@@ -73,6 +81,10 @@ export const attestSlice = createSlice({
setIsCreating: (state, action: PayloadAction) => {
state.isCreating = action.payload;
},
+ setCreateTx: (state, action: PayloadAction) => {
+ state.createTx = action.payload;
+ state.isCreating = false;
+ },
reset: (state) => ({
...initialState,
sourceChain: state.sourceChain,
@@ -88,9 +100,11 @@ export const {
setSourceChain,
setSourceAsset,
setTargetChain,
+ setAttestTx,
setSignedVAAHex,
setIsSending,
setIsCreating,
+ setCreateTx,
reset,
} = attestSlice.actions;
diff --git a/bridge_ui/src/store/selectors.ts b/bridge_ui/src/store/selectors.ts
index e9d433f6..33e38846 100644
--- a/bridge_ui/src/store/selectors.ts
+++ b/bridge_ui/src/store/selectors.ts
@@ -15,12 +15,14 @@ export const selectAttestSourceAsset = (state: RootState) =>
state.attest.sourceAsset;
export const selectAttestTargetChain = (state: RootState) =>
state.attest.targetChain;
+export const selectAttestAttestTx = (state: RootState) => state.attest.attestTx;
export const selectAttestSignedVAAHex = (state: RootState) =>
state.attest.signedVAAHex;
export const selectAttestIsSending = (state: RootState) =>
state.attest.isSending;
export const selectAttestIsCreating = (state: RootState) =>
state.attest.isCreating;
+export const selectAttestCreateTx = (state: RootState) => state.attest.createTx;
export const selectAttestIsSourceComplete = (state: RootState) =>
!!state.attest.sourceChain && !!state.attest.sourceAsset;
// TODO: check wrapped asset exists or is native attest
@@ -28,6 +30,8 @@ export const selectAttestIsTargetComplete = (state: RootState) =>
selectAttestIsSourceComplete(state) && !!state.attest.targetChain;
export const selectAttestIsSendComplete = (state: RootState) =>
!!selectAttestSignedVAAHex(state);
+export const selectAttestIsCreateComplete = (state: RootState) =>
+ !!selectAttestCreateTx(state);
export const selectAttestShouldLockFields = (state: RootState) =>
selectAttestIsSending(state) || selectAttestIsSendComplete(state);