UI - added circle loader and swap progress bar
This commit is contained in:
parent
76f4c63198
commit
89c118a37f
|
@ -1 +1,3 @@
|
||||||
.env
|
.env
|
||||||
|
build/
|
||||||
|
node_modules/
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "Cross Chain Swap",
|
"name": "NativeSwap",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>Cross Chain Swap</title>
|
<title>NativeSwap</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import "../css/CircleLoader.css";
|
||||||
|
|
||||||
|
export default function CircleLoader() {
|
||||||
|
return (
|
||||||
|
<div id={"loaderContainer"}>
|
||||||
|
<div id={"circle"}>
|
||||||
|
<div id={"inner"}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { ChainId, CHAIN_ID_POLYGON, isEVMChain } from "@certusone/wormhole-sdk";
|
||||||
|
import { LinearProgress, makeStyles, Typography } from "@material-ui/core";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default function TransactionProgress({
|
||||||
|
chainId,
|
||||||
|
txBlockNumber,
|
||||||
|
step,
|
||||||
|
}: {
|
||||||
|
chainId: ChainId;
|
||||||
|
txBlockNumber: number | undefined;
|
||||||
|
step: number;
|
||||||
|
}) {
|
||||||
|
const classes = useStyles();
|
||||||
|
const { provider } = useEthereumProvider();
|
||||||
|
const [currentBlock, setCurrentBlock] = useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
if (step !== 1 || !txBlockNumber) return;
|
||||||
|
if (isEVMChain(chainId) && provider) {
|
||||||
|
let cancelled = false;
|
||||||
|
(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [step, chainId, provider, txBlockNumber]);
|
||||||
|
const blockDiff =
|
||||||
|
txBlockNumber !== undefined && txBlockNumber && currentBlock
|
||||||
|
? currentBlock - txBlockNumber
|
||||||
|
: 0;
|
||||||
|
const expectedBlocks = 15;
|
||||||
|
let value;
|
||||||
|
let valueBuffer;
|
||||||
|
let message;
|
||||||
|
switch (step) {
|
||||||
|
case 1:
|
||||||
|
value = (blockDiff / expectedBlocks) * 50;
|
||||||
|
valueBuffer = 50;
|
||||||
|
message = `Waiting for ${blockDiff} / ${expectedBlocks} confirmations on ${
|
||||||
|
chainId === CHAIN_ID_POLYGON ? "Polygon" : "Ethereum"
|
||||||
|
}...`;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
value = 50;
|
||||||
|
valueBuffer = 100;
|
||||||
|
message = "Waiting for relayer to complete swap...";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
value = 100;
|
||||||
|
valueBuffer = 100;
|
||||||
|
message = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
|
<LinearProgress
|
||||||
|
variant="buffer"
|
||||||
|
value={value}
|
||||||
|
valueBuffer={valueBuffer}
|
||||||
|
/>
|
||||||
|
<Typography variant="body2" className={classes.message}>
|
||||||
|
{message}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
:root {
|
||||||
|
--basis: linear-gradient(
|
||||||
|
160deg,
|
||||||
|
rgba(69, 74, 117, 0.473) 0%,
|
||||||
|
rgba(98, 104, 143, 0.445) 100%
|
||||||
|
),
|
||||||
|
linear-gradient(
|
||||||
|
45deg,
|
||||||
|
rgba(153, 69, 255, 0.411) 0%,
|
||||||
|
rgba(0, 209, 139, 0.404) 100%
|
||||||
|
);
|
||||||
|
/* --gradient1: rgb(26, 212, 150); */
|
||||||
|
/* --gradient2: rgb(176, 139, 221); */
|
||||||
|
--gradient1: rgb(117, 228, 187);
|
||||||
|
--gradient2: rgb(193, 164, 230);
|
||||||
|
}
|
||||||
|
|
||||||
|
#loaderContainer {
|
||||||
|
display: flex;
|
||||||
|
width: max-content;
|
||||||
|
height: max-content;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
@keyframes rotation {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes switch {
|
||||||
|
0% {
|
||||||
|
top: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
box-shadow: 0 -130px 0 -75px var(--gradient1);
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
top: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
box-shadow: 0 -130px 0 -75px var(--gradient1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
top: calc(100% - 55px);
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
box-shadow: 0 -130px 0 75px var(--gradient1);
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
top: calc(100% - 55px);
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
box-shadow: 0 -130px 0 75px var(--gradient1);
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
top: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
box-shadow: 0 -130px 0 -75px var(--gradient1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#circle {
|
||||||
|
width: 325px;
|
||||||
|
height: 325px;
|
||||||
|
display: block;
|
||||||
|
background: var(--basis);
|
||||||
|
border-radius: 500%;
|
||||||
|
position: relative;
|
||||||
|
animation: rotation 2s linear infinite;
|
||||||
|
}
|
||||||
|
#inner {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
background: var(--gradient2);
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
border-radius: 100%;
|
||||||
|
box-shadow: 0 -130px 0 -75px #222;
|
||||||
|
animation: switch 8s ease-in-out infinite;
|
||||||
|
}
|
|
@ -17,7 +17,6 @@ export default async function getIsTransferCompletedEvmWithRetry(
|
||||||
provider,
|
provider,
|
||||||
signedVAA
|
signedVAA
|
||||||
);
|
);
|
||||||
console.log("getIsTransferCompletedEth", result);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
Collapse,
|
||||||
Container,
|
Container,
|
||||||
Link,
|
Link,
|
||||||
makeStyles,
|
makeStyles,
|
||||||
|
@ -33,6 +34,9 @@ import { Alert } from "@material-ui/lab";
|
||||||
import parseError from "../utils/parseError";
|
import parseError from "../utils/parseError";
|
||||||
import Settings from "../components/Settings";
|
import Settings from "../components/Settings";
|
||||||
import getIsTransferCompletedEvmWithRetry from "../utils/getIsTransferCompletedWithRetry";
|
import getIsTransferCompletedEvmWithRetry from "../utils/getIsTransferCompletedWithRetry";
|
||||||
|
import CircleLoader from "../components/CircleLoader";
|
||||||
|
import { ArrowForward, CheckCircleOutlineRounded } from "@material-ui/icons";
|
||||||
|
import SwapProgress from "../components/SwapProgress";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
bg: {
|
bg: {
|
||||||
|
@ -115,6 +119,16 @@ const useStyles = makeStyles((theme) => ({
|
||||||
margin: "1rem",
|
margin: "1rem",
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
},
|
},
|
||||||
|
loaderHolder: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
successIcon: {
|
||||||
|
color: COLORS.green,
|
||||||
|
fontSize: "200px",
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const switchProviderNetwork = async (
|
const switchProviderNetwork = async (
|
||||||
|
@ -136,10 +150,11 @@ const switchProviderNetwork = async (
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const [sourceTokenInfo, setSourceTokenInfo] = useState(WMATIC_TOKEN_INFO);
|
const [sourceTokenInfo, setSourceTokenInfo] = useState(MATIC_TOKEN_INFO);
|
||||||
const [targetTokenInfo, setTargetTokenInfo] = useState(WETH_TOKEN_INFO);
|
const [targetTokenInfo, setTargetTokenInfo] = useState(ETH_TOKEN_INFO);
|
||||||
const [amountIn, setAmountIn] = useState("");
|
const [amountIn, setAmountIn] = useState("");
|
||||||
const [amountOut, setAmountOut] = useState("0.0");
|
const [amountInUST, setAmountInUST] = useState("");
|
||||||
|
const [amountOut, setAmountOut] = useState("");
|
||||||
const [deadline, setDeadline] = useState("30");
|
const [deadline, setDeadline] = useState("30");
|
||||||
const [slippage, setSlippage] = useState("1");
|
const [slippage, setSlippage] = useState("1");
|
||||||
const [executor, setExecutor] = useState<UniswapToUniswapExecutor | null>(
|
const [executor, setExecutor] = useState<UniswapToUniswapExecutor | null>(
|
||||||
|
@ -150,18 +165,25 @@ export default function Home() {
|
||||||
const [hasQuote, setHasQuote] = useState(false);
|
const [hasQuote, setHasQuote] = useState(false);
|
||||||
const { provider, signer } = useEthereumProvider();
|
const { provider, signer } = useEthereumProvider();
|
||||||
const { enqueueSnackbar } = useSnackbar();
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
|
const [isFirstSwapComplete, setIsFirstSwapComplete] = useState(false);
|
||||||
|
const [isSecondSwapComplete, setIsSecondSwapComplete] = useState(false);
|
||||||
|
const [sourceTxBlockNumber, setSourceTxBlockNumber] = useState<
|
||||||
|
number | undefined
|
||||||
|
>(undefined);
|
||||||
|
const [hasSignedVAA, setHasSignedVAA] = useState(false);
|
||||||
|
const [relayerTimeoutString, setRelayerTimeoutString] = useState("");
|
||||||
|
|
||||||
const computeQuote = useCallback(() => {
|
const computeQuote = useCallback(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
setHasQuote(false);
|
setHasQuote(false);
|
||||||
setIsComputingQuote(true);
|
setIsComputingQuote(true);
|
||||||
|
setAmountOut("");
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
parseFloat(amountIn) > 0 &&
|
parseFloat(amountIn) > 0 &&
|
||||||
!isNaN(parseFloat(deadline)) &&
|
!isNaN(parseFloat(deadline)) &&
|
||||||
!isNaN(parseFloat(slippage))
|
!isNaN(parseFloat(slippage))
|
||||||
) {
|
) {
|
||||||
setAmountOut("0.0");
|
|
||||||
const executor = new UniswapToUniswapExecutor();
|
const executor = new UniswapToUniswapExecutor();
|
||||||
await executor.initialize(
|
await executor.initialize(
|
||||||
sourceTokenInfo.address,
|
sourceTokenInfo.address,
|
||||||
|
@ -184,9 +206,12 @@ export default function Home() {
|
||||||
executor.tokens.dstOut.formatAmount(quote.dst.minAmountOut)
|
executor.tokens.dstOut.formatAmount(quote.dst.minAmountOut)
|
||||||
).toFixed(8)
|
).toFixed(8)
|
||||||
);
|
);
|
||||||
|
setAmountInUST(
|
||||||
|
parseFloat(
|
||||||
|
executor.tokens.dstIn.formatAmount(quote.dst.amountIn)
|
||||||
|
).toFixed(2)
|
||||||
|
);
|
||||||
setHasQuote(true);
|
setHasQuote(true);
|
||||||
} else {
|
|
||||||
setAmountOut("0.0");
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
@ -246,13 +271,28 @@ export default function Home() {
|
||||||
setTargetTokenInfo(ETH_TOKEN_INFO);
|
setTargetTokenInfo(ETH_TOKEN_INFO);
|
||||||
}
|
}
|
||||||
setAmountIn("");
|
setAmountIn("");
|
||||||
setAmountOut("0.0");
|
setAmountOut("");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const reset = useCallback(() => {
|
||||||
|
setIsSwapping(false);
|
||||||
|
setHasQuote(false);
|
||||||
|
setIsFirstSwapComplete(false);
|
||||||
|
setIsSecondSwapComplete(false);
|
||||||
|
setAmountIn("");
|
||||||
|
setAmountOut("");
|
||||||
|
setSourceTxBlockNumber(undefined);
|
||||||
|
setRelayerTimeoutString("");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSwapClick = useCallback(async () => {
|
const handleSwapClick = useCallback(async () => {
|
||||||
if (provider && signer && executor) {
|
if (provider && signer && executor) {
|
||||||
try {
|
try {
|
||||||
setIsSwapping(true);
|
setIsSwapping(true);
|
||||||
|
setIsFirstSwapComplete(false);
|
||||||
|
setHasSignedVAA(false);
|
||||||
|
setIsSecondSwapComplete(false);
|
||||||
|
setRelayerTimeoutString("");
|
||||||
await switchProviderNetwork(provider, sourceTokenInfo.chainId);
|
await switchProviderNetwork(provider, sourceTokenInfo.chainId);
|
||||||
|
|
||||||
const sourceReceipt = await executor.approveAndSwap(signer);
|
const sourceReceipt = await executor.approveAndSwap(signer);
|
||||||
|
@ -260,24 +300,17 @@ export default function Home() {
|
||||||
"firstSwapTransactionHash:",
|
"firstSwapTransactionHash:",
|
||||||
sourceReceipt.transactionHash
|
sourceReceipt.transactionHash
|
||||||
);
|
);
|
||||||
|
setIsFirstSwapComplete(true);
|
||||||
|
setSourceTxBlockNumber(sourceReceipt.blockNumber);
|
||||||
|
|
||||||
// Wait for the guardian network to reach consensus and emit the signedVAA
|
// Wait for the guardian network to reach consensus and emit the signedVAA
|
||||||
enqueueSnackbar(null, {
|
|
||||||
content: <Alert severity="info">Fetching VAA</Alert>,
|
|
||||||
});
|
|
||||||
const { vaaBytes } = await getSignedVAAWithRetry(
|
const { vaaBytes } = await getSignedVAAWithRetry(
|
||||||
WORMHOLE_RPC_HOSTS,
|
WORMHOLE_RPC_HOSTS,
|
||||||
executor.srcExecutionParams.wormhole.chainId,
|
executor.srcExecutionParams.wormhole.chainId,
|
||||||
executor.vaaSearchParams.emitterAddress,
|
executor.vaaSearchParams.emitterAddress,
|
||||||
executor.vaaSearchParams.sequence
|
executor.vaaSearchParams.sequence
|
||||||
);
|
);
|
||||||
enqueueSnackbar(null, {
|
setHasSignedVAA(true);
|
||||||
content: (
|
|
||||||
<Alert severity="info">
|
|
||||||
Fetched the Signed VAA, waiting for relayer to redeem it
|
|
||||||
</Alert>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
// Check if the signedVAA has redeemed by the relayer
|
// Check if the signedVAA has redeemed by the relayer
|
||||||
const isCompleted = await getIsTransferCompletedEvmWithRetry(
|
const isCompleted = await getIsTransferCompletedEvmWithRetry(
|
||||||
executor.dstExecutionParams.wormhole.tokenBridgeAddress,
|
executor.dstExecutionParams.wormhole.tokenBridgeAddress,
|
||||||
|
@ -287,30 +320,26 @@ export default function Home() {
|
||||||
3000,
|
3000,
|
||||||
40
|
40
|
||||||
);
|
);
|
||||||
if (isCompleted) {
|
if (!isCompleted) {
|
||||||
enqueueSnackbar(null, {
|
|
||||||
content: <Alert severity="success">Swap completed</Alert>,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// If the relayer hasn't redeemed the signedVAA, then manually redeem it ourselves
|
// If the relayer hasn't redeemed the signedVAA, then manually redeem it ourselves
|
||||||
|
setRelayerTimeoutString(
|
||||||
|
"Timed out waiting for relayer to complete swap. You'll need to complete it yourself."
|
||||||
|
);
|
||||||
await switchProviderNetwork(provider, targetTokenInfo.chainId);
|
await switchProviderNetwork(provider, targetTokenInfo.chainId);
|
||||||
const targetReceipt = await executor.fetchVaaAndSwap(signer);
|
const targetReceipt = await executor.fetchVaaAndSwap(signer);
|
||||||
enqueueSnackbar(null, {
|
|
||||||
content: <Alert severity="success">Swap completed</Alert>,
|
|
||||||
});
|
|
||||||
console.info(
|
console.info(
|
||||||
"secondSwapTransactionHash:",
|
"secondSwapTransactionHash:",
|
||||||
targetReceipt.transactionHash
|
targetReceipt.transactionHash
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
setIsSecondSwapComplete(true);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
reset();
|
||||||
console.error(e);
|
console.error(e);
|
||||||
enqueueSnackbar(null, {
|
enqueueSnackbar(null, {
|
||||||
content: <Alert severity="error">{parseError(e)}</Alert>,
|
content: <Alert severity="error">{parseError(e)}</Alert>,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setIsSwapping(false);
|
|
||||||
setAmountIn("");
|
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
provider,
|
provider,
|
||||||
|
@ -319,6 +348,7 @@ export default function Home() {
|
||||||
enqueueSnackbar,
|
enqueueSnackbar,
|
||||||
sourceTokenInfo,
|
sourceTokenInfo,
|
||||||
targetTokenInfo,
|
targetTokenInfo,
|
||||||
|
reset,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const readyToSwap = provider && signer && hasQuote;
|
const readyToSwap = provider && signer && hasQuote;
|
||||||
|
@ -328,10 +358,11 @@ export default function Home() {
|
||||||
<Container className={classes.centeredContainer} maxWidth="sm">
|
<Container className={classes.centeredContainer} maxWidth="sm">
|
||||||
<div className={classes.titleBar}></div>
|
<div className={classes.titleBar}></div>
|
||||||
<Typography variant="h4" color="textSecondary">
|
<Typography variant="h4" color="textSecondary">
|
||||||
Cross Chain Swap Demo
|
Wormhole NativeSwap Demo
|
||||||
</Typography>
|
</Typography>
|
||||||
<div className={classes.spacer} />
|
<div className={classes.spacer} />
|
||||||
<Paper className={classes.mainPaper}>
|
<Paper className={classes.mainPaper}>
|
||||||
|
<Collapse in={!isFirstSwapComplete}>
|
||||||
<Settings
|
<Settings
|
||||||
disabled={isSwapping || isComputingQuote}
|
disabled={isSwapping || isComputingQuote}
|
||||||
slippage={slippage}
|
slippage={slippage}
|
||||||
|
@ -370,6 +401,7 @@ export default function Home() {
|
||||||
InputProps={{ disableUnderline: true }}
|
InputProps={{ disableUnderline: true }}
|
||||||
className={classes.numberField}
|
className={classes.numberField}
|
||||||
inputProps={{ readOnly: true }}
|
inputProps={{ readOnly: true }}
|
||||||
|
placeholder="0.0"
|
||||||
></TextField>
|
></TextField>
|
||||||
<Typography variant="subtitle2">{`Slippage tolerance: ${slippage}%`}</Typography>
|
<Typography variant="subtitle2">{`Slippage tolerance: ${slippage}%`}</Typography>
|
||||||
{!isSwapping && <EthereumSignerKey />}
|
{!isSwapping && <EthereumSignerKey />}
|
||||||
|
@ -380,6 +412,49 @@ export default function Home() {
|
||||||
>
|
>
|
||||||
Swap
|
Swap
|
||||||
</ButtonWithLoader>
|
</ButtonWithLoader>
|
||||||
|
</Collapse>
|
||||||
|
<Collapse in={isFirstSwapComplete && !isSecondSwapComplete}>
|
||||||
|
<div className={classes.loaderHolder}>
|
||||||
|
<CircleLoader />
|
||||||
|
<div className={classes.spacer} />
|
||||||
|
<Typography variant="h5">
|
||||||
|
{`Your ${sourceTokenInfo.name} is being swapped to ${targetTokenInfo.name}`}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</Collapse>
|
||||||
|
<Collapse in={isSecondSwapComplete}>
|
||||||
|
<div className={classes.loaderHolder}>
|
||||||
|
<CheckCircleOutlineRounded
|
||||||
|
className={classes.successIcon}
|
||||||
|
fontSize={"inherit"}
|
||||||
|
/>
|
||||||
|
<Typography>Swap completed!</Typography>
|
||||||
|
<ButtonWithLoader onClick={() => reset()}>
|
||||||
|
Swap more tokens!
|
||||||
|
</ButtonWithLoader>
|
||||||
|
</div>
|
||||||
|
</Collapse>
|
||||||
|
<div className={classes.spacer} />
|
||||||
|
{hasQuote && (
|
||||||
|
<Typography variant="subtitle1">
|
||||||
|
{`${amountIn} ${sourceTokenInfo.name} `}
|
||||||
|
<ArrowForward fontSize="inherit" />
|
||||||
|
{` ${amountInUST} UST `} <ArrowForward fontSize="inherit" />
|
||||||
|
{` ${amountOut} ${targetTokenInfo.name}`}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
{isFirstSwapComplete &&
|
||||||
|
!isSecondSwapComplete &&
|
||||||
|
!relayerTimeoutString && (
|
||||||
|
<SwapProgress
|
||||||
|
chainId={sourceTokenInfo.chainId}
|
||||||
|
txBlockNumber={sourceTxBlockNumber}
|
||||||
|
step={!hasSignedVAA ? 1 : !isSecondSwapComplete ? 2 : 3}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{relayerTimeoutString && (
|
||||||
|
<Typography variant="subtitle1">{relayerTimeoutString}</Typography>
|
||||||
|
)}
|
||||||
<div className={classes.spacer} />
|
<div className={classes.spacer} />
|
||||||
<Typography variant="subtitle2" color="error">
|
<Typography variant="subtitle2" color="error">
|
||||||
WARNING: this is a Testnet release only
|
WARNING: this is a Testnet release only
|
||||||
|
|
Loading…
Reference in New Issue