bridge_ui: show confirmation progress
Change-Id: I48d1a176e7263ab4727c403f80edcca856e00b95
This commit is contained in:
parent
b5745a063e
commit
a94e014918
|
@ -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 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue