bridge_ui: show confirmation progress

Change-Id: I48d1a176e7263ab4727c403f80edcca856e00b95
This commit is contained in:
Evan Gray 2021-09-01 17:21:35 -04:00
parent b5745a063e
commit a94e014918
5 changed files with 127 additions and 1 deletions

View File

@ -10,6 +10,7 @@ import { CHAINS_BY_ID } from "../../utils/consts";
import ButtonWithLoader from "../ButtonWithLoader";
import KeyAndBalance from "../KeyAndBalance";
import StepDescription from "../StepDescription";
import TransferProgress from "./TransferProgress";
function Send() {
const { handleClick, disabled, showLoader } = useHandleTransfer();
@ -36,6 +37,7 @@ function Send() {
>
Transfer
</ButtonWithLoader>
<TransferProgress />
</>
);
}

View File

@ -0,0 +1,102 @@
import { 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 {
selectTransferIsSendComplete,
selectTransferSourceChain,
selectTransferTransferTx,
} from "../../store/selectors";
import { CHAINS_BY_ID, SOLANA_HOST } from "../../utils/consts";
const useStyles = makeStyles((theme) => ({
root: {
marginTop: theme.spacing(2),
textAlign: "center",
},
message: {
marginTop: theme.spacing(1),
},
}));
export default function TransferProgress() {
const classes = useStyles();
const sourceChain = useSelector(selectTransferSourceChain);
const transferTx = useSelector(selectTransferTransferTx);
const isSendComplete = useSelector(selectTransferIsSendComplete);
const { provider } = useEthereumProvider();
const [currentBlock, setCurrentBlock] = useState(0);
useEffect(() => {
if (isSendComplete || !transferTx) return;
let cancelled = false;
if (sourceChain === CHAIN_ID_ETH && provider) {
(async () => {
while (!cancelled) {
await new Promise((resolve) => setTimeout(resolve, 500));
try {
const newBlock = await provider.getBlockNumber();
if (!cancelled) {
setCurrentBlock(newBlock);
}
} catch (e) {
console.error(e);
}
}
})();
}
if (sourceChain === CHAIN_ID_SOLANA) {
(async () => {
// TODO: share connection in context?
const connection = new Connection(SOLANA_HOST, "confirmed");
while (!cancelled) {
await new Promise((resolve) => setTimeout(resolve, 200));
try {
const newBlock = await connection.getSlot();
if (!cancelled) {
setCurrentBlock(newBlock);
}
} catch (e) {
console.error(e);
}
}
})();
}
return () => {
cancelled = true;
};
}, [isSendComplete, sourceChain, provider, transferTx]);
const blockDiff =
transferTx && transferTx.block && currentBlock
? currentBlock - transferTx.block
: undefined;
const expectedBlocks =
sourceChain === CHAIN_ID_SOLANA
? 32
: sourceChain === CHAIN_ID_ETH
? 15
: 1;
if (
!isSendComplete &&
(sourceChain === CHAIN_ID_SOLANA || sourceChain === CHAIN_ID_ETH) &&
blockDiff !== undefined
) {
return (
<div className={classes.root}>
<LinearProgress
value={
blockDiff < expectedBlocks ? (blockDiff / expectedBlocks) * 75 : 75
}
variant="determinate"
/>
<Typography variant="body2" className={classes.message}>
{blockDiff < expectedBlocks
? `Waiting for ${blockDiff} / ${expectedBlocks} confirmations on ${CHAINS_BY_ID[sourceChain].name}...`
: `Waiting for Wormhole Network consensus...`}
</Typography>
</div>
);
}
return null;
}

View File

@ -38,7 +38,11 @@ import {
selectTransferSourceParsedTokenAccount,
selectTransferTargetChain,
} from "../store/selectors";
import { setIsSending, setSignedVAAHex } from "../store/transferSlice";
import {
setIsSending,
setSignedVAAHex,
setTransferTx,
} from "../store/transferSlice";
import { hexToUint8Array, uint8ArrayToHex } from "../utils/array";
import {
ETH_BRIDGE_ADDRESS,
@ -75,6 +79,9 @@ async function eth(
recipientChain,
recipientAddress
);
dispatch(
setTransferTx({ 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);
@ -135,6 +142,7 @@ async function solana(
if (!info) {
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(
@ -184,6 +192,7 @@ async function terra(
console.log(result);
const info = await waitForTerraExecution(result);
console.log(info);
dispatch(setTransferTx({ id: info.txhash, block: info.height }));
enqueueSnackbar("Transaction confirmed", { variant: "success" });
const sequence = parseSequenceFromLogTerra(info);
console.log(sequence);

View File

@ -65,6 +65,8 @@ export const selectTransferTargetParsedTokenAccount = (state: RootState) =>
state.transfer.targetParsedTokenAccount;
export const selectTransferTargetBalanceString = (state: RootState) =>
state.transfer.targetParsedTokenAccount?.uiAmountString || "";
export const selectTransferTransferTx = (state: RootState) =>
state.transfer.transferTx;
export const selectTransferSignedVAAHex = (state: RootState) =>
state.transfer.signedVAAHex;
export const selectTransferIsSending = (state: RootState) =>

View File

@ -26,6 +26,11 @@ export interface ParsedTokenAccount {
uiAmountString: string;
}
export interface Transaction {
id: string;
block: number;
}
export interface TransferState {
activeStep: Steps;
sourceChain: ChainId;
@ -39,6 +44,7 @@ export interface TransferState {
targetAddressHex: string | undefined;
targetAsset: string | null | undefined;
targetParsedTokenAccount: ParsedTokenAccount | undefined;
transferTx: Transaction | undefined;
signedVAAHex: string | undefined;
isSending: boolean;
isRedeeming: boolean;
@ -57,6 +63,7 @@ const initialState: TransferState = {
targetAddressHex: undefined,
targetAsset: undefined,
targetParsedTokenAccount: undefined,
transferTx: undefined,
signedVAAHex: undefined,
isSending: false,
isRedeeming: false,
@ -155,6 +162,9 @@ export const transferSlice = createSlice({
) => {
state.targetParsedTokenAccount = action.payload;
},
setTransferTx: (state, action: PayloadAction<Transaction>) => {
state.transferTx = action.payload;
},
setSignedVAAHex: (state, action: PayloadAction<string>) => {
state.signedVAAHex = action.payload;
state.isSending = false;
@ -185,6 +195,7 @@ export const {
setTargetAddressHex,
setTargetAsset,
setTargetParsedTokenAccount,
setTransferTx,
setSignedVAAHex,
setIsSending,
setIsRedeeming,