|
|
@@ -7,7 +7,6 @@ import {
|
|
|
getEmitterAddressEth,
|
|
|
getEmitterAddressSolana,
|
|
|
getEmitterAddressTerra,
|
|
|
- getSignedVAA,
|
|
|
parseSequenceFromLogEth,
|
|
|
parseSequenceFromLogSolana,
|
|
|
parseSequenceFromLogTerra,
|
|
|
@@ -15,6 +14,7 @@ import {
|
|
|
import {
|
|
|
Box,
|
|
|
Button,
|
|
|
+ CircularProgress,
|
|
|
Dialog,
|
|
|
DialogActions,
|
|
|
DialogContent,
|
|
|
@@ -32,6 +32,7 @@ import { Alert } from "@material-ui/lab";
|
|
|
import { Connection } from "@solana/web3.js";
|
|
|
import { LCDClient } from "@terra-money/terra.js";
|
|
|
import { BigNumber, ethers } from "ethers";
|
|
|
+import { useSnackbar } from "notistack";
|
|
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
|
import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
|
|
|
@@ -59,7 +60,8 @@ import {
|
|
|
TERRA_TOKEN_BRIDGE_ADDRESS,
|
|
|
WORMHOLE_RPC_HOSTS,
|
|
|
} from "../../utils/consts";
|
|
|
-import { getNextRpcHost } from "../../utils/getSignedVAAWithRetry";
|
|
|
+import { getSignedVAAWithRetry } from "../../utils/getSignedVAAWithRetry";
|
|
|
+import parseError from "../../utils/parseError";
|
|
|
import KeyAndBalance from "../KeyAndBalance";
|
|
|
|
|
|
const useStyles = makeStyles((theme) => ({
|
|
|
@@ -70,25 +72,30 @@ const useStyles = makeStyles((theme) => ({
|
|
|
},
|
|
|
}));
|
|
|
|
|
|
-async function eth(provider: ethers.providers.Web3Provider, tx: string) {
|
|
|
+async function eth(
|
|
|
+ provider: ethers.providers.Web3Provider,
|
|
|
+ tx: string,
|
|
|
+ enqueueSnackbar: any
|
|
|
+) {
|
|
|
try {
|
|
|
const receipt = await provider.getTransactionReceipt(tx);
|
|
|
const sequence = parseSequenceFromLogEth(receipt, ETH_BRIDGE_ADDRESS);
|
|
|
const emitterAddress = getEmitterAddressEth(ETH_TOKEN_BRIDGE_ADDRESS);
|
|
|
- const { vaaBytes } = await getSignedVAA(
|
|
|
- WORMHOLE_RPC_HOSTS[getNextRpcHost()],
|
|
|
+ const { vaaBytes } = await getSignedVAAWithRetry(
|
|
|
CHAIN_ID_ETH,
|
|
|
emitterAddress,
|
|
|
- sequence.toString()
|
|
|
+ sequence.toString(),
|
|
|
+ WORMHOLE_RPC_HOSTS.length
|
|
|
);
|
|
|
- return uint8ArrayToHex(vaaBytes);
|
|
|
+ return { vaa: uint8ArrayToHex(vaaBytes), error: null };
|
|
|
} catch (e) {
|
|
|
console.error(e);
|
|
|
+ enqueueSnackbar(parseError(e), { variant: "error" });
|
|
|
+ return { vaa: null, error: parseError(e) };
|
|
|
}
|
|
|
- return "";
|
|
|
}
|
|
|
|
|
|
-async function solana(tx: string) {
|
|
|
+async function solana(tx: string, enqueueSnackbar: any) {
|
|
|
try {
|
|
|
const connection = new Connection(SOLANA_HOST, "confirmed");
|
|
|
const info = await connection.getTransaction(tx);
|
|
|
@@ -99,20 +106,21 @@ async function solana(tx: string) {
|
|
|
const emitterAddress = await getEmitterAddressSolana(
|
|
|
SOL_TOKEN_BRIDGE_ADDRESS
|
|
|
);
|
|
|
- const { vaaBytes } = await getSignedVAA(
|
|
|
- WORMHOLE_RPC_HOSTS[getNextRpcHost()],
|
|
|
+ const { vaaBytes } = await getSignedVAAWithRetry(
|
|
|
CHAIN_ID_SOLANA,
|
|
|
emitterAddress,
|
|
|
- sequence.toString()
|
|
|
+ sequence.toString(),
|
|
|
+ WORMHOLE_RPC_HOSTS.length
|
|
|
);
|
|
|
- return uint8ArrayToHex(vaaBytes);
|
|
|
+ return { vaa: uint8ArrayToHex(vaaBytes), error: null };
|
|
|
} catch (e) {
|
|
|
console.error(e);
|
|
|
+ enqueueSnackbar(parseError(e), { variant: "error" });
|
|
|
+ return { vaa: null, error: parseError(e) };
|
|
|
}
|
|
|
- return "";
|
|
|
}
|
|
|
|
|
|
-async function terra(tx: string) {
|
|
|
+async function terra(tx: string, enqueueSnackbar: any) {
|
|
|
try {
|
|
|
const lcd = new LCDClient(TERRA_HOST);
|
|
|
const info = await lcd.tx.txInfo(tx);
|
|
|
@@ -123,17 +131,18 @@ async function terra(tx: string) {
|
|
|
const emitterAddress = await getEmitterAddressTerra(
|
|
|
TERRA_TOKEN_BRIDGE_ADDRESS
|
|
|
);
|
|
|
- const { vaaBytes } = await getSignedVAA(
|
|
|
- WORMHOLE_RPC_HOSTS[getNextRpcHost()],
|
|
|
+ const { vaaBytes } = await getSignedVAAWithRetry(
|
|
|
CHAIN_ID_TERRA,
|
|
|
emitterAddress,
|
|
|
- sequence
|
|
|
+ sequence,
|
|
|
+ WORMHOLE_RPC_HOSTS.length
|
|
|
);
|
|
|
- return uint8ArrayToHex(vaaBytes);
|
|
|
+ return { vaa: uint8ArrayToHex(vaaBytes), error: null };
|
|
|
} catch (e) {
|
|
|
console.error(e);
|
|
|
+ enqueueSnackbar(parseError(e), { variant: "error" });
|
|
|
+ return { vaa: null, error: parseError(e) };
|
|
|
}
|
|
|
- return "";
|
|
|
}
|
|
|
|
|
|
// 0 u256 amount
|
|
|
@@ -159,12 +168,16 @@ function RecoveryDialogContent({
|
|
|
onClose: () => void;
|
|
|
disabled: boolean;
|
|
|
}) {
|
|
|
+ const { enqueueSnackbar } = useSnackbar();
|
|
|
const dispatch = useDispatch();
|
|
|
const { provider } = useEthereumProvider();
|
|
|
const currentSourceChain = useSelector(selectTransferSourceChain);
|
|
|
const [recoverySourceChain, setRecoverySourceChain] =
|
|
|
useState(currentSourceChain);
|
|
|
const [recoverySourceTx, setRecoverySourceTx] = useState("");
|
|
|
+ const [recoverySourceTxIsLoading, setRecoverySourceTxIsLoading] =
|
|
|
+ useState(false);
|
|
|
+ const [recoverySourceTxError, setRecoverySourceTxError] = useState("");
|
|
|
const currentSignedVAA = useSelector(selectTransferSignedVAAHex);
|
|
|
const [recoverySignedVAA, setRecoverySignedVAA] = useState(currentSignedVAA);
|
|
|
const [recoveryParsedVAA, setRecoveryParsedVAA] = useState<any>(null);
|
|
|
@@ -178,24 +191,55 @@ function RecoveryDialogContent({
|
|
|
if (recoverySourceTx) {
|
|
|
let cancelled = false;
|
|
|
if (recoverySourceChain === CHAIN_ID_ETH && provider) {
|
|
|
+ setRecoverySourceTxError("");
|
|
|
+ setRecoverySourceTxIsLoading(true);
|
|
|
(async () => {
|
|
|
- const vaa = await eth(provider, recoverySourceTx);
|
|
|
+ const { vaa, error } = await eth(
|
|
|
+ provider,
|
|
|
+ recoverySourceTx,
|
|
|
+ enqueueSnackbar
|
|
|
+ );
|
|
|
if (!cancelled) {
|
|
|
- setRecoverySignedVAA(vaa);
|
|
|
+ setRecoverySourceTxIsLoading(false);
|
|
|
+ if (vaa) {
|
|
|
+ setRecoverySignedVAA(vaa);
|
|
|
+ }
|
|
|
+ if (error) {
|
|
|
+ setRecoverySourceTxError(error);
|
|
|
+ }
|
|
|
}
|
|
|
})();
|
|
|
} else if (recoverySourceChain === CHAIN_ID_SOLANA) {
|
|
|
+ setRecoverySourceTxError("");
|
|
|
+ setRecoverySourceTxIsLoading(true);
|
|
|
(async () => {
|
|
|
- const vaa = await solana(recoverySourceTx);
|
|
|
+ const { vaa, error } = await solana(
|
|
|
+ recoverySourceTx,
|
|
|
+ enqueueSnackbar
|
|
|
+ );
|
|
|
if (!cancelled) {
|
|
|
- setRecoverySignedVAA(vaa);
|
|
|
+ setRecoverySourceTxIsLoading(false);
|
|
|
+ if (vaa) {
|
|
|
+ setRecoverySignedVAA(vaa);
|
|
|
+ }
|
|
|
+ if (error) {
|
|
|
+ setRecoverySourceTxError(error);
|
|
|
+ }
|
|
|
}
|
|
|
})();
|
|
|
} else if (recoverySourceChain === CHAIN_ID_TERRA) {
|
|
|
+ setRecoverySourceTxError("");
|
|
|
+ setRecoverySourceTxIsLoading(true);
|
|
|
(async () => {
|
|
|
- const vaa = await terra(recoverySourceTx);
|
|
|
+ const { vaa, error } = await terra(recoverySourceTx, enqueueSnackbar);
|
|
|
if (!cancelled) {
|
|
|
- setRecoverySignedVAA(vaa);
|
|
|
+ setRecoverySourceTxIsLoading(false);
|
|
|
+ if (vaa) {
|
|
|
+ setRecoverySignedVAA(vaa);
|
|
|
+ }
|
|
|
+ if (error) {
|
|
|
+ setRecoverySourceTxError(error);
|
|
|
+ }
|
|
|
}
|
|
|
})();
|
|
|
}
|
|
|
@@ -203,7 +247,7 @@ function RecoveryDialogContent({
|
|
|
cancelled = true;
|
|
|
};
|
|
|
}
|
|
|
- }, [recoverySourceChain, recoverySourceTx, provider]);
|
|
|
+ }, [recoverySourceChain, recoverySourceTx, provider, enqueueSnackbar]);
|
|
|
useEffect(() => {
|
|
|
setRecoverySignedVAA(currentSignedVAA);
|
|
|
}, [currentSignedVAA]);
|
|
|
@@ -212,10 +256,10 @@ function RecoveryDialogContent({
|
|
|
setRecoverySourceChain(event.target.value);
|
|
|
}, []);
|
|
|
const handleSourceTxChange = useCallback((event) => {
|
|
|
- setRecoverySourceTx(event.target.value);
|
|
|
+ setRecoverySourceTx(event.target.value.trim());
|
|
|
}, []);
|
|
|
const handleSignedVAAChange = useCallback((event) => {
|
|
|
- setRecoverySignedVAA(event.target.value);
|
|
|
+ setRecoverySignedVAA(event.target.value.trim());
|
|
|
}, []);
|
|
|
useEffect(() => {
|
|
|
let cancelled = false;
|
|
|
@@ -292,23 +336,45 @@ function RecoveryDialogContent({
|
|
|
<KeyAndBalance chainId={recoverySourceChain} />
|
|
|
) : null}
|
|
|
<TextField
|
|
|
- label="Source Tx"
|
|
|
- disabled={!!recoverySignedVAA}
|
|
|
+ label="Source Tx (paste here)"
|
|
|
+ disabled={!!recoverySignedVAA || recoverySourceTxIsLoading}
|
|
|
value={recoverySourceTx}
|
|
|
onChange={handleSourceTxChange}
|
|
|
+ error={!!recoverySourceTxError}
|
|
|
+ helperText={recoverySourceTxError}
|
|
|
fullWidth
|
|
|
margin="normal"
|
|
|
/>
|
|
|
- <Box mt={4}>
|
|
|
- <Typography>or</Typography>
|
|
|
+ <Box position="relative">
|
|
|
+ <Box mt={4}>
|
|
|
+ <Typography>or</Typography>
|
|
|
+ </Box>
|
|
|
+ <TextField
|
|
|
+ label="Signed VAA (Hex)"
|
|
|
+ disabled={recoverySourceTxIsLoading}
|
|
|
+ value={recoverySignedVAA || ""}
|
|
|
+ onChange={handleSignedVAAChange}
|
|
|
+ fullWidth
|
|
|
+ margin="normal"
|
|
|
+ />
|
|
|
+ {recoverySourceTxIsLoading ? (
|
|
|
+ <Box
|
|
|
+ position="absolute"
|
|
|
+ style={{
|
|
|
+ top: 0,
|
|
|
+ right: 0,
|
|
|
+ left: 0,
|
|
|
+ bottom: 0,
|
|
|
+ backgroundColor: "rgba(0,0,0,0.5)",
|
|
|
+ display: "flex",
|
|
|
+ alignItems: "center",
|
|
|
+ justifyContent: "center",
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <CircularProgress />
|
|
|
+ </Box>
|
|
|
+ ) : null}
|
|
|
</Box>
|
|
|
- <TextField
|
|
|
- label="Signed VAA (Hex)"
|
|
|
- value={recoverySignedVAA || ""}
|
|
|
- onChange={handleSignedVAAChange}
|
|
|
- fullWidth
|
|
|
- margin="normal"
|
|
|
- />
|
|
|
<Box my={4}>
|
|
|
<Divider />
|
|
|
</Box>
|