UI - terra support

This commit is contained in:
Kevin Peters 2022-01-26 17:07:33 +00:00
parent 0c50e93dc0
commit 1c3b6908a9
15 changed files with 489 additions and 231 deletions

103
react/package-lock.json generated
View File

@ -14,6 +14,7 @@
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
"@metamask/detect-provider": "^1.2.0",
"@terra-money/wallet-provider": "^2.2.0",
"@types/node": "^16.11.19",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
@ -4464,34 +4465,17 @@
"react-dom": "^17.0.0"
}
},
"node_modules/@terra-dev/web-connector-controller": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@terra-dev/web-connector-controller/-/web-connector-controller-0.8.1.tgz",
"integrity": "sha512-TIwFtta7vN2GdDUy8SbIIsfTd9XWiU+U6yjizd82yUAhlOJNAGHshG0r1j0irkA5MycYG1duAlr7foeKRu4PGA==",
"node_modules/@terra-dev/web-extension": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@terra-dev/web-extension/-/web-extension-0.6.0.tgz",
"integrity": "sha512-IyIWHLfweZCb5nHuMyzavnMYposnZMvpsA/89zZPIgIooxhxE//uZD+Ty+ptt4nvkbOgEFKdKIKe5rIHqgVLpA==",
"dependencies": {
"@terra-dev/web-connector-interface": "^0.8.1",
"@terra-money/terra.js": "^1.8.0 || ^2.0.0",
"bowser": "^2.11.0",
"rxjs": "^7.4.0"
"rxjs": "^7.3.0"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"@terra-money/terra.js": "^2.0.0"
}
},
"node_modules/@terra-dev/web-connector-interface": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@terra-dev/web-connector-interface/-/web-connector-interface-0.8.1.tgz",
"integrity": "sha512-ryA3xtTFJ7OkAF6pTlrsuqxtSUp0DxHhyxvzwRPbT3h8VqlkFStknvYjRwNRspN2LOpi4/F1TNFzcUBNHPCo2g==",
"dependencies": {
"rxjs": "^7.4.0"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"@terra-money/terra.js": "^2.0.0"
}
},
"node_modules/@terra-money/terra.js": {
@ -4536,27 +4520,26 @@
}
},
"node_modules/@terra-money/wallet-provider": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/@terra-money/wallet-provider/-/wallet-provider-2.5.3.tgz",
"integrity": "sha512-v/5Z35gCo4nZyZCu3nYDFvhwuvlyDeNSSYmN9KUc9ewoIO9K/2fi3vxcOLcvqq5PYowwwod21vgaQ9QHFV+8eA==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@terra-money/wallet-provider/-/wallet-provider-2.2.0.tgz",
"integrity": "sha512-K8NLpJ/yak8Pq6jQpjVr7yWDIbxjTp42OXaAS+xlTufqQwWbCR7coAGbm2FpYX43j4uymuSAICZvsOS1qrFeYA==",
"dependencies": {
"@terra-dev/browser-check": "^2.5.3",
"@terra-dev/chrome-extension": "^2.5.3",
"@terra-dev/readonly-wallet": "^2.5.3",
"@terra-dev/readonly-wallet-modal": "^2.5.3",
"@terra-dev/use-wallet": "^2.5.3",
"@terra-dev/wallet-types": "^2.5.3",
"@terra-dev/walletconnect": "^2.5.3",
"@terra-dev/web-connector-controller": "^0.8.1",
"@terra-dev/web-connector-interface": "^0.8.1",
"@terra-dev/browser-check": "^2.2.0",
"@terra-dev/chrome-extension": "^2.2.0",
"@terra-dev/readonly-wallet": "^2.2.0",
"@terra-dev/readonly-wallet-modal": "^2.2.0",
"@terra-dev/use-wallet": "^2.2.0",
"@terra-dev/wallet-types": "^2.2.0",
"@terra-dev/walletconnect": "^2.2.0",
"@terra-dev/web-extension": "^0.6.0",
"@terra-money/terra.js": "^2.0.0",
"fast-deep-equal": "^3.1.3",
"rxjs": "^7.4.0"
"rxjs": "^7.3.0"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"@terra-money/terra.js": "^2.0.0",
"react": "^17.0.0"
},
"peerDependenciesMeta": {
@ -29130,22 +29113,14 @@
"styled-components": "^5.0.0"
}
},
"@terra-dev/web-connector-controller": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@terra-dev/web-connector-controller/-/web-connector-controller-0.8.1.tgz",
"integrity": "sha512-TIwFtta7vN2GdDUy8SbIIsfTd9XWiU+U6yjizd82yUAhlOJNAGHshG0r1j0irkA5MycYG1duAlr7foeKRu4PGA==",
"@terra-dev/web-extension": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@terra-dev/web-extension/-/web-extension-0.6.0.tgz",
"integrity": "sha512-IyIWHLfweZCb5nHuMyzavnMYposnZMvpsA/89zZPIgIooxhxE//uZD+Ty+ptt4nvkbOgEFKdKIKe5rIHqgVLpA==",
"requires": {
"@terra-dev/web-connector-interface": "^0.8.1",
"@terra-money/terra.js": "^1.8.0 || ^2.0.0",
"bowser": "^2.11.0",
"rxjs": "^7.4.0"
}
},
"@terra-dev/web-connector-interface": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@terra-dev/web-connector-interface/-/web-connector-interface-0.8.1.tgz",
"integrity": "sha512-ryA3xtTFJ7OkAF6pTlrsuqxtSUp0DxHhyxvzwRPbT3h8VqlkFStknvYjRwNRspN2LOpi4/F1TNFzcUBNHPCo2g==",
"requires": {
"rxjs": "^7.4.0"
"rxjs": "^7.3.0"
}
},
"@terra-money/terra.js": {
@ -29189,21 +29164,21 @@
}
},
"@terra-money/wallet-provider": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/@terra-money/wallet-provider/-/wallet-provider-2.5.3.tgz",
"integrity": "sha512-v/5Z35gCo4nZyZCu3nYDFvhwuvlyDeNSSYmN9KUc9ewoIO9K/2fi3vxcOLcvqq5PYowwwod21vgaQ9QHFV+8eA==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@terra-money/wallet-provider/-/wallet-provider-2.2.0.tgz",
"integrity": "sha512-K8NLpJ/yak8Pq6jQpjVr7yWDIbxjTp42OXaAS+xlTufqQwWbCR7coAGbm2FpYX43j4uymuSAICZvsOS1qrFeYA==",
"requires": {
"@terra-dev/browser-check": "^2.5.3",
"@terra-dev/chrome-extension": "^2.5.3",
"@terra-dev/readonly-wallet": "^2.5.3",
"@terra-dev/readonly-wallet-modal": "^2.5.3",
"@terra-dev/use-wallet": "^2.5.3",
"@terra-dev/wallet-types": "^2.5.3",
"@terra-dev/walletconnect": "^2.5.3",
"@terra-dev/web-connector-controller": "^0.8.1",
"@terra-dev/web-connector-interface": "^0.8.1",
"@terra-dev/browser-check": "^2.2.0",
"@terra-dev/chrome-extension": "^2.2.0",
"@terra-dev/readonly-wallet": "^2.2.0",
"@terra-dev/readonly-wallet-modal": "^2.2.0",
"@terra-dev/use-wallet": "^2.2.0",
"@terra-dev/wallet-types": "^2.2.0",
"@terra-dev/walletconnect": "^2.2.0",
"@terra-dev/web-extension": "^0.6.0",
"@terra-money/terra.js": "^2.0.0",
"fast-deep-equal": "^3.1.3",
"rxjs": "^7.4.0"
"rxjs": "^7.3.0"
}
},
"@tootallnate/once": {

View File

@ -10,6 +10,7 @@
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
"@metamask/detect-provider": "^1.2.0",
"@terra-money/wallet-provider": "^2.2.0",
"@types/node": "^16.11.19",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",

View File

@ -16,17 +16,21 @@ const useStyles = makeStyles((theme) => ({
export default function TransactionProgress({
chainId,
txBlockNumber,
step,
isSourceSwapComplete,
hasSignedVAA,
isTargetSwapComplete,
}: {
chainId: ChainId;
txBlockNumber: number | undefined;
step: number;
isSourceSwapComplete: boolean;
hasSignedVAA: boolean;
isTargetSwapComplete: boolean;
}) {
const classes = useStyles();
const { provider } = useEthereumProvider();
const [currentBlock, setCurrentBlock] = useState(0);
useEffect(() => {
if (step !== 1 || !txBlockNumber) return;
if (hasSignedVAA || !txBlockNumber) return;
if (isEVMChain(chainId) && provider) {
let cancelled = false;
(async () => {
@ -46,7 +50,7 @@ export default function TransactionProgress({
cancelled = true;
};
}
}, [step, chainId, provider, txBlockNumber]);
}, [hasSignedVAA, chainId, provider, txBlockNumber]);
const blockDiff =
txBlockNumber !== undefined && txBlockNumber && currentBlock
? currentBlock - txBlockNumber
@ -55,24 +59,20 @@ export default function TransactionProgress({
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;
if (!hasSignedVAA) {
value = (blockDiff / expectedBlocks) * 50;
valueBuffer = 50;
message = `Waiting for ${blockDiff} / ${expectedBlocks} confirmations on ${
chainId === CHAIN_ID_POLYGON ? "Polygon" : "Ethereum"
}...`;
} else if (!isTargetSwapComplete) {
value = 50;
valueBuffer = 100;
message = "Waiting for relayer to complete swap...";
} else {
value = 100;
valueBuffer = 100;
message = "Success!";
}
return (
<div className={classes.root}>

View File

@ -0,0 +1,22 @@
import { useTerraWallet } from "../contexts/TerraWalletContext";
import ToggleConnectedButton from "./ToggleConnectedButton";
const TerraWalletKey = () => {
const { connect, disconnect, connected, wallet } = useTerraWallet();
const pk =
(wallet &&
wallet.wallets &&
wallet.wallets.length > 0 &&
wallet.wallets[0].terraAddress) ||
"";
return (
<ToggleConnectedButton
connect={connect}
disconnect={disconnect}
connected={connected}
pk={pk}
/>
);
};
export default TerraWalletKey;

View File

@ -0,0 +1,97 @@
import {
NetworkInfo,
Wallet,
WalletProvider,
useWallet,
} from "@terra-money/wallet-provider";
import React, {
ReactChildren,
useCallback,
useContext,
useMemo,
useState,
} from "react";
const testnet: NetworkInfo = {
name: "testnet",
chainID: "bombay-12",
lcd: "https://bombay-lcd.terra.dev",
};
const walletConnectChainIds: Record<number, NetworkInfo> = {
0: testnet,
};
interface ITerraWalletContext {
connect(): void;
disconnect(): void;
connected: boolean;
wallet: any;
}
const TerraWalletContext = React.createContext<ITerraWalletContext>({
connect: () => {},
disconnect: () => {},
connected: false,
wallet: null,
});
export const TerraWalletWrapper = ({
children,
}: {
children: ReactChildren;
}) => {
// TODO: Use wallet instead of useConnectedWallet.
const terraWallet = useWallet();
const [, setWallet] = useState<Wallet | undefined>(undefined);
const [connected, setConnected] = useState(false);
const connect = useCallback(() => {
const CHROME_EXTENSION = 1;
if (terraWallet) {
terraWallet.connect(terraWallet.availableConnectTypes[CHROME_EXTENSION]);
setWallet(terraWallet);
setConnected(true);
}
}, [terraWallet]);
const disconnect = useCallback(() => {
setConnected(false);
setWallet(undefined);
}, []);
const contextValue = useMemo(
() => ({
connect,
disconnect,
connected,
wallet: terraWallet,
}),
[connect, disconnect, connected, terraWallet]
);
return (
<TerraWalletContext.Provider value={contextValue}>
{children}
</TerraWalletContext.Provider>
);
};
export const TerraWalletProvider = ({
children,
}: {
children: ReactChildren;
}) => {
return (
<WalletProvider
defaultNetwork={testnet}
walletConnectChainIds={walletConnectChainIds}
>
<TerraWalletWrapper>{children}</TerraWalletWrapper>
</WalletProvider>
);
};
export const useTerraWallet = () => {
return useContext(TerraWalletContext);
};

View File

@ -1,5 +1,11 @@
import { ChainId, CHAIN_ID_SOLANA, isEVMChain } from "@certusone/wormhole-sdk";
import {
ChainId,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
isEVMChain,
} from "@certusone/wormhole-sdk";
import { hexlify, hexStripZeros } from "@ethersproject/bytes";
import { useConnectedWallet } from "@terra-money/wallet-provider";
import { useCallback, useMemo } from "react";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
// import { useSolanaWallet } from "../contexts/SolanaWalletContext";
@ -31,6 +37,8 @@ function useIsWalletReady(
const autoSwitch = enableNetworkAutoswitch;
// const solanaWallet = useSolanaWallet();
// const solPK = solanaWallet?.publicKey;
const terraWallet = useConnectedWallet();
const hasTerraWallet = !!terraWallet;
const {
provider,
signerAddress,
@ -54,15 +62,28 @@ function useIsWalletReady(
}, [provider, correctEvmNetwork, chainId]);
return useMemo(() => {
//if (chainId === CHAIN_ID_SOLANA && solPK) {
// return createWalletStatus(
// true,
// undefined,
// forceNetworkSwitch,
// solPK.toString()
// );
//}
if (
chainId === CHAIN_ID_TERRA &&
hasTerraWallet &&
terraWallet?.walletAddress
) {
// TODO: terraWallet does not update on wallet changes
return createWalletStatus(
true,
undefined,
forceNetworkSwitch,
terraWallet.walletAddress
);
}
if (isEVMChain(chainId) && hasEthInfo && signerAddress) {
//if (chainId === CHAIN_ID_SOLANA && solPK) {
// return createWalletStatus(
// true,
// undefined,
// forceNetworkSwitch,
// solPK.toString()
// );
//}
if (hasCorrectEvmNetwork) {
return createWalletStatus(
true,
@ -93,12 +114,14 @@ function useIsWalletReady(
chainId,
autoSwitch,
forceNetworkSwitch,
hasTerraWallet,
// solPK,
hasEthInfo,
correctEvmNetwork,
hasCorrectEvmNetwork,
provider,
signerAddress,
terraWallet,
]);
}

23
react/src/icons/terra.svg Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 288.9 274" style="enable-background:new 0 0 288.9 274;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#2849A9;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#5795ED;}
</style>
<path class="st0" d="M151.1,0.3c33.7,0,64.9,12.1,88.7,32.9c31.8,24.5,22.6,113.9-9.6,90.3c-70.8-0.3-202.4-38.2-163.2-90.3
c4-5.3,9-9.6,14.5-13.7h-0.3c0.9-0.5,1.9-1,2.8-1.6c0.9-0.5,1.9-1.1,2.8-1.6h0c2.8-1.6,5.6-3.1,8.7-4.3
C112.5,4.6,131.3,0.3,151.1,0.3z M174.9,272.8c-14.2,0.9-42.6-21.4-50.7-50.9c-15.1-55.9,107.2-84.4,118.7-85.4
c31.2,0.9,38.9,38.2,16.1,76.7C229.3,262.6,175.5,272.8,174.9,272.8z"/>
<path class="st1" d="M14.8,77.9c9.9,2.8,70.5-16.5,88.4-43.8c0.3-0.3,14.2-21.7-12.7-22c-3.1,0-11.7,0.3-20.1,5.3
c-4,2.5-7.7,5-11.4,7.8c-5.8,4.3-11.3,9.5-16.5,14.4h0l-0.2,0.2c-5.3,5-10.2,10.9-14.5,16.8c-4.3,5.9-8.3,12.4-11.7,18.9
c-0.2,0.5-0.4,0.9-0.6,1.2C15.2,77,15,77.4,14.8,77.9z M86.5,272.8c1.9-2.8,3.1-36.6,1.9-45.3c-1.2-8.7-4-26.4-20.7-55.6
c-2.8-4.7-16.1-26.4-26-39.7c-5.6-7.8-11.7-15-17.8-22.3h0c-5.1-6-10.2-12.1-15-18.4c-0.3,0.8-0.5,1.5-0.8,2.2s-0.5,1.4-0.8,2.2
c-2.5,7.1-4.3,14.6-5.6,22.4S0,133.8,0,141.8c0,8.1,0.6,15.8,1.9,23.6s3.4,15.2,5.6,22.4c2.2,7.1,5.3,14.3,8.7,20.8
s7.4,13,11.7,18.9c4.3,5.9,9.3,11.5,14.5,16.8c4.9,5.3,10.8,10.2,16.7,14.6h0h0c4.6,3.1,9.3,6.2,13.9,9c8.5,5,11.7,5,13.4,5
C86.4,272.8,86.4,272.8,86.5,272.8z M288.9,141.8c0,18.9-3.7,36.9-10.2,53.4c-15.7,17-115.3-20.7-130.8-26.6c-1.2-0.5-2-0.7-2-0.8
c-15.8-6.8-63.3-27.9-67.7-60.8c-6.2-47.5,89.6-80.7,131.9-82c4.9,0,20.4,0.3,29.4,7.5C269.8,59.2,288.9,98.4,288.9,141.8z
M188.8,260.1c-3.7,12.1,10.2,16.5,22.6,10.6c24.7-13,45.1-33.2,59-57.1c0.9-1.2,0-2.5-1.5-2.2C255.6,212.6,195.6,236.5,188.8,260.1
z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -5,6 +5,7 @@ import ReactDOM from "react-dom";
import App from "./App";
import ErrorBoundary from "./components/ErrorBoundary";
import { EthereumProviderProvider } from "./contexts/EthereumProviderContext";
import { TerraWalletProvider } from "./contexts/TerraWalletContext";
import { theme } from "./muiTheme";
ReactDOM.render(
@ -12,9 +13,11 @@ ReactDOM.render(
<ThemeProvider theme={theme}>
<CssBaseline>
<EthereumProviderProvider>
<SnackbarProvider maxSnack={3}>
<App />
</SnackbarProvider>
<TerraWalletProvider>
<SnackbarProvider maxSnack={3}>
<App />
</SnackbarProvider>
</TerraWalletProvider>
</EthereumProviderProvider>
</CssBaseline>
</ThemeProvider>

View File

@ -7,9 +7,6 @@ import {
WETH_TOKEN_INFO,
WMATIC_TOKEN_INFO,
UST_TOKEN_INFO,
WORMHOLE_CHAIN_ID_ETHEREUM,
WORMHOLE_CHAIN_ID_POLYGON,
WORMHOLE_CHAIN_ID_TERRA,
} from "../utils/consts";
import { addFixedAmounts, subtractFixedAmounts } from "../utils/math";
import { UstLocation } from "./generic";
@ -19,7 +16,12 @@ import {
makeExactInParameters,
makeExactOutParameters,
} from "./uniswap-core";
import { ChainId } from "@certusone/wormhole-sdk";
import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_POLYGON,
CHAIN_ID_TERRA,
} from "@certusone/wormhole-sdk";
export { PROTOCOL as PROTOCOL_UNISWAP_V2 } from "./uniswap-v2";
export { PROTOCOL as PROTOCOL_UNISWAP_V3 } from "./uniswap-v3";
@ -57,13 +59,13 @@ export function makeEvmProviderFromAddress(tokenAddress: string) {
export function getChainIdFromAddress(tokenAddress: string) {
switch (tokenAddress) {
case WETH_TOKEN_INFO.address: {
return WORMHOLE_CHAIN_ID_ETHEREUM;
return CHAIN_ID_ETH;
}
case WMATIC_TOKEN_INFO.address: {
return WORMHOLE_CHAIN_ID_POLYGON;
return CHAIN_ID_POLYGON;
}
case UST_TOKEN_INFO.address: {
return WORMHOLE_CHAIN_ID_TERRA;
return CHAIN_ID_TERRA;
}
default: {
throw Error("unrecognized evm token address");
@ -110,16 +112,16 @@ export interface RelayerFee {
export interface ExactInCrossParameters {
amountIn: string;
minAmountOut: string;
src: ExactInParameters;
dst: ExactInParameters;
src: ExactInParameters | undefined;
dst: ExactInParameters | undefined;
relayerFee: RelayerFee;
}
export interface ExactOutCrossParameters {
amountOut: string;
maxAmountIn: string;
src: ExactOutParameters;
dst: ExactOutParameters;
src: ExactOutParameters | undefined;
dst: ExactOutParameters | undefined;
relayerFee: RelayerFee;
}
@ -132,22 +134,16 @@ export class UniswapToUniswapQuoter {
srcRouter: UstRouter | EthRouter | MaticRouter;
dstRouter: UstRouter | EthRouter | MaticRouter;
constructor() {}
async initialize(
tokenInAddress: string,
tokenOutAddress: string
): Promise<void> {
async initialize(tokenInAddress: string, tokenOutAddress: string) {
if (tokenInAddress !== this.tokenInAddress) {
this.tokenInAddress = tokenInAddress;
this.srcRouter = await makeRouter(tokenInAddress, UstLocation.Out);
}
if (tokenOutAddress != this.tokenOutAddress) {
if (tokenOutAddress !== this.tokenOutAddress) {
this.tokenOutAddress = tokenOutAddress;
this.dstRouter = await makeRouter(tokenOutAddress, UstLocation.In);
}
return;
}
async computeAndVerifySrcPoolAddress(): Promise<string> {
@ -158,7 +154,7 @@ export class UniswapToUniswapQuoter {
return this.dstRouter.computeAndVerifyPoolAddress();
}
computeSwapSlippage(slippage): string {
computeSwapSlippage(slippage: string): string {
if (this.isSrcUst() || this.isDstUst()) {
return slippage;
}
@ -184,7 +180,7 @@ export class UniswapToUniswapQuoter {
makeSrcExactInParameters(
amountIn: string,
minAmountOut: string
): ExactInParameters {
): ExactInParameters | undefined {
if (this.isSrcUst()) {
return undefined;
}
@ -195,7 +191,7 @@ export class UniswapToUniswapQuoter {
makeDstExactInParameters(
amountIn: string,
minAmountOut: string
): ExactInParameters {
): ExactInParameters | undefined {
if (this.isDstUst()) {
return undefined;
}
@ -251,9 +247,9 @@ export class UniswapToUniswapQuoter {
makeSrcExactOutParameters(
amountOut: string,
maxAmountIn: string
): ExactOutParameters {
): ExactOutParameters | undefined {
if (this.isSrcUst()) {
return null;
return undefined;
}
// @ts-ignore
return makeExactOutParameters(this.srcRouter, amountOut, maxAmountIn);
@ -262,9 +258,9 @@ export class UniswapToUniswapQuoter {
makeDstExactOutParameters(
amountOut: string,
maxAmountIn: string
): ExactOutParameters {
): ExactOutParameters | undefined {
if (this.isDstUst()) {
return null;
return undefined;
}
// @ts-ignore
return makeExactOutParameters(this.dstRouter, amountOut, maxAmountIn);
@ -337,7 +333,7 @@ export class UniswapToUniswapQuoter {
return this.tokenOutAddress === TERRA_UST;
}
getSrcEvmProvider(): ethers.providers.Provider {
getSrcEvmProvider(): ethers.providers.Provider | undefined {
if (this.isSrcUst()) {
return undefined;
}
@ -345,7 +341,7 @@ export class UniswapToUniswapQuoter {
return this.srcRouter.getProvider();
}
getDstEvmProvider(): ethers.providers.Provider {
getDstEvmProvider(): ethers.providers.Provider | undefined {
if (this.isDstUst()) {
return undefined;
}

View File

@ -3,7 +3,7 @@ import { ethers } from "ethers";
import { GenericToken } from "./generic";
// erc20 spec
import { abi as Erc20Abi } from "../../abi/erc20.json";
import { abi as Erc20Abi } from "../abi/erc20.json";
import {
TransactionReceipt,
TransactionRequest,

View File

@ -1,3 +1,4 @@
//@ts-nocheck
import { ethers } from "ethers";
import { CurrencyAmount, Token } from "@uniswap/sdk-core";

View File

@ -1,14 +1,17 @@
//@ts-nocheck
import { ethers } from "ethers";
import { TransactionReceipt } from "@ethersproject/abstract-provider";
import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_POLYGON,
CHAIN_ID_TERRA,
getEmitterAddressEth,
hexToUint8Array,
nativeToHexString,
parseSequenceFromLogEth,
//getSignedVAAWithRetry,
getSignedVAAWithRetry,
} from "@certusone/wormhole-sdk";
import getSignedVAAWithRetry from "@certusone/wormhole-sdk/lib/cjs/rpc/getSignedVAAWithRetry";
import { grpc } from "@improbable-eng/grpc-web";
import { UniEvmToken } from "../route/uniswap-core";
import {
@ -26,9 +29,6 @@ import {
CORE_BRIDGE_ADDRESS_ETHEREUM,
CORE_BRIDGE_ADDRESS_POLYGON,
CORE_BRIDGE_ADDRESS_TERRA,
WORMHOLE_CHAIN_ID_ETHEREUM,
WORMHOLE_CHAIN_ID_POLYGON,
WORMHOLE_CHAIN_ID_TERRA,
WORMHOLE_RPC_HOSTS,
//ETH_NETWORK_CHAIN_ID,
//POLYGON_NETWORK_CHAIN_ID,
@ -44,10 +44,10 @@ import {
swapExactOutFromVaaNative,
swapExactOutFromVaaToken,
} from "./util";
import { abi as SWAP_CONTRACT_V2_ABI } from "../../abi/contracts/CrossChainSwapV2.json";
import { abi as SWAP_CONTRACT_V3_ABI } from "../../abi/contracts/CrossChainSwapV3.json";
import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_ETHEREUM } from "../../scripts/contract-addresses/goerli";
import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_POLYGON } from "../../scripts/contract-addresses/mumbai";
import { abi as SWAP_CONTRACT_V2_ABI } from "../abi/contracts/CrossChainSwapV2.json";
import { abi as SWAP_CONTRACT_V3_ABI } from "../abi/contracts/CrossChainSwapV3.json";
import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_ETHEREUM } from "../addresses/goerli";
import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_POLYGON } from "../addresses/mumbai";
// placeholders
const CROSSCHAINSWAP_CONTRACT_ADDRESS_TERRA = "";
@ -72,7 +72,7 @@ const EXECUTION_PARAMETERS_ETHEREUM: ExecutionParameters = {
address: CROSSCHAINSWAP_CONTRACT_ADDRESS_ETHEREUM,
},
wormhole: {
chainId: WORMHOLE_CHAIN_ID_ETHEREUM,
chainId: CHAIN_ID_ETH,
coreBridgeAddress: CORE_BRIDGE_ADDRESS_ETHEREUM,
tokenBridgeAddress: TOKEN_BRIDGE_ADDRESS_ETHEREUM,
},
@ -83,7 +83,7 @@ const EXECUTION_PARAMETERS_POLYGON: ExecutionParameters = {
address: CROSSCHAINSWAP_CONTRACT_ADDRESS_POLYGON,
},
wormhole: {
chainId: WORMHOLE_CHAIN_ID_POLYGON,
chainId: CHAIN_ID_POLYGON,
coreBridgeAddress: CORE_BRIDGE_ADDRESS_POLYGON,
tokenBridgeAddress: TOKEN_BRIDGE_ADDRESS_POLYGON,
},
@ -94,7 +94,7 @@ const EXECUTION_PARAMETERS_TERRA: ExecutionParameters = {
address: CROSSCHAINSWAP_CONTRACT_ADDRESS_TERRA,
},
wormhole: {
chainId: WORMHOLE_CHAIN_ID_TERRA,
chainId: CHAIN_ID_TERRA,
coreBridgeAddress: CORE_BRIDGE_ADDRESS_TERRA,
tokenBridgeAddress: TOKEN_BRIDGE_ADDRESS_TERRA,
},
@ -102,13 +102,13 @@ const EXECUTION_PARAMETERS_TERRA: ExecutionParameters = {
function makeExecutionParameters(chainId: ChainId): ExecutionParameters {
switch (chainId) {
case WORMHOLE_CHAIN_ID_ETHEREUM: {
case CHAIN_ID_ETH: {
return EXECUTION_PARAMETERS_ETHEREUM;
}
case WORMHOLE_CHAIN_ID_POLYGON: {
case CHAIN_ID_POLYGON: {
return EXECUTION_PARAMETERS_POLYGON;
}
case WORMHOLE_CHAIN_ID_TERRA: {
case CHAIN_ID_TERRA: {
return EXECUTION_PARAMETERS_TERRA;
}
default: {

View File

@ -1,21 +1,12 @@
import {
ChainId,
CHAIN_ID_ETH as WORMHOLE_CHAIN_ID_ETHEREUM,
CHAIN_ID_POLYGON as WORMHOLE_CHAIN_ID_POLYGON,
CHAIN_ID_TERRA as WORMHOLE_CHAIN_ID_TERRA,
CHAIN_ID_ETH,
CHAIN_ID_POLYGON,
CHAIN_ID_TERRA,
} from "@certusone/wormhole-sdk";
//import ethIcon from "../icons/eth.svg";
//import polygonIcon from "../icons/polygon.svg";
const ethIcon = undefined;
const polygonIcon = undefined;
const ustIcon = undefined;
export {
WORMHOLE_CHAIN_ID_ETHEREUM,
WORMHOLE_CHAIN_ID_POLYGON,
WORMHOLE_CHAIN_ID_TERRA,
};
import ethIcon from "../icons/eth.svg";
import polygonIcon from "../icons/polygon.svg";
import terraIcon from "../icons/terra.svg";
export interface TokenInfo {
name: string;
@ -24,13 +15,13 @@ export interface TokenInfo {
logo: string;
isNative: boolean;
maxAmount: number;
ustPairedAddress: string;
ustPairedAddress: string | undefined;
}
export const MATIC_TOKEN_INFO: TokenInfo = {
name: "MATIC",
address: "0x9c3c9283d3e44854697cd22d3faa240cfb032889", // used to compute quote
chainId: WORMHOLE_CHAIN_ID_POLYGON,
chainId: CHAIN_ID_POLYGON,
logo: polygonIcon,
isNative: true,
maxAmount: 0.1,
@ -40,7 +31,7 @@ export const MATIC_TOKEN_INFO: TokenInfo = {
export const WMATIC_TOKEN_INFO: TokenInfo = {
name: "WMATIC",
address: "0x9c3c9283d3e44854697cd22d3faa240cfb032889",
chainId: WORMHOLE_CHAIN_ID_POLYGON,
chainId: CHAIN_ID_POLYGON,
logo: polygonIcon,
isNative: false,
maxAmount: 0.1,
@ -50,7 +41,7 @@ export const WMATIC_TOKEN_INFO: TokenInfo = {
export const ETH_TOKEN_INFO: TokenInfo = {
name: "ETH",
address: "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6", // used to compute quote
chainId: WORMHOLE_CHAIN_ID_ETHEREUM,
chainId: CHAIN_ID_ETH,
logo: ethIcon,
isNative: true,
maxAmount: 0.01,
@ -60,7 +51,7 @@ export const ETH_TOKEN_INFO: TokenInfo = {
export const WETH_TOKEN_INFO: TokenInfo = {
name: "WETH",
address: "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6",
chainId: WORMHOLE_CHAIN_ID_ETHEREUM,
chainId: CHAIN_ID_ETH,
logo: ethIcon,
isNative: false,
maxAmount: 0.01,
@ -70,9 +61,9 @@ export const WETH_TOKEN_INFO: TokenInfo = {
export const UST_TOKEN_INFO: TokenInfo = {
name: "UST",
address: "uusd",
chainId: WORMHOLE_CHAIN_ID_TERRA,
logo: ustIcon,
isNative: false,
chainId: CHAIN_ID_TERRA,
logo: terraIcon,
isNative: true, // TODO: change?
maxAmount: 10.0,
ustPairedAddress: undefined,
};
@ -85,18 +76,33 @@ export const TOKEN_INFOS = [
UST_TOKEN_INFO,
];
// evm handling
export const EVM_ETH_NETWORK_CHAIN_ID = 5;
export const EVM_POLYGON_NETWORK_CHAIN_ID = 80001;
export const getSupportedSwaps = (tokenInfo: TokenInfo) => {
switch (tokenInfo) {
case MATIC_TOKEN_INFO:
return [ETH_TOKEN_INFO, UST_TOKEN_INFO];
case WMATIC_TOKEN_INFO:
return [WETH_TOKEN_INFO];
case ETH_TOKEN_INFO:
return [MATIC_TOKEN_INFO, UST_TOKEN_INFO];
case WETH_TOKEN_INFO:
return [WMATIC_TOKEN_INFO];
case UST_TOKEN_INFO:
return [ETH_TOKEN_INFO, MATIC_TOKEN_INFO];
}
return [];
};
export const ETH_NETWORK_CHAIN_ID = 5;
export const POLYGON_NETWORK_CHAIN_ID = 80001;
export const getEvmChainId = (chainId: ChainId) =>
chainId === WORMHOLE_CHAIN_ID_ETHEREUM
? EVM_ETH_NETWORK_CHAIN_ID
: chainId === WORMHOLE_CHAIN_ID_POLYGON
? EVM_POLYGON_NETWORK_CHAIN_ID
chainId === CHAIN_ID_ETH
? ETH_NETWORK_CHAIN_ID
: chainId === CHAIN_ID_POLYGON
? POLYGON_NETWORK_CHAIN_ID
: undefined;
// misc
export const RELAYER_FEE_UST = "0.25";
export const WORMHOLE_RPC_HOSTS = [

View File

@ -6,7 +6,7 @@ export function addFixedAmounts(
decimals: number
): string {
const sum = FixedNumber.from(left).addUnsafe(FixedNumber.from(right));
return sum.round(this.getDecimals()).toString();
return sum.round(decimals).toString();
}
export function subtractFixedAmounts(

View File

@ -7,7 +7,12 @@ import {
TextField,
Typography,
} from "@material-ui/core";
import { ChainId, getSignedVAAWithRetry } from "@certusone/wormhole-sdk";
import {
ChainId,
CHAIN_ID_TERRA,
getSignedVAAWithRetry,
isEVMChain,
} from "@certusone/wormhole-sdk";
import { useCallback, useEffect, useState } from "react";
import ButtonWithLoader from "../components/ButtonWithLoader";
import EthereumSignerKey from "../components/EthereumSignerKey";
@ -16,11 +21,11 @@ import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import {
ETH_TOKEN_INFO,
getEvmChainId,
getSupportedSwaps,
MATIC_TOKEN_INFO,
RELAYER_FEE_UST,
TokenInfo,
TOKEN_INFOS,
WETH_TOKEN_INFO,
WMATIC_TOKEN_INFO,
WORMHOLE_RPC_HOSTS,
} from "../utils/consts";
import { COLORS } from "../muiTheme";
@ -34,9 +39,15 @@ import parseError from "../utils/parseError";
import Settings from "../components/Settings";
import getIsTransferCompletedEvmWithRetry from "../utils/getIsTransferCompletedWithRetry";
import CircleLoader from "../components/CircleLoader";
import { ArrowForward, CheckCircleOutlineRounded } from "@material-ui/icons";
import {
ArrowForward,
CheckCircleOutlineRounded,
QueueTwoTone,
} from "@material-ui/icons";
import SwapProgress from "../components/SwapProgress";
import Footer from "../components/Footer";
import TerraWalletKey from "../components/TerraWalletKey";
import useIsWalletReady from "../hooks/useIsWalletReady";
const useStyles = makeStyles((theme) => ({
bg: {
@ -135,7 +146,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
const switchProviderNetwork = async (
const switchEvmProviderNetwork = async (
provider: Web3Provider,
chainId: ChainId
) => {
@ -152,6 +163,74 @@ const switchProviderNetwork = async (
}
};
const ConnectedWalletAddress = ({ chainId }: { chainId: ChainId }) => {
const { walletAddress } = useIsWalletReady(chainId, false);
if (walletAddress) {
const is0x = walletAddress.startsWith("0x");
return (
<Typography variant="subtitle2">
{walletAddress?.substring(0, is0x ? 6 : 3)}...
{walletAddress?.substring(walletAddress.length - (is0x ? 4 : 3))}
</Typography>
);
}
return null;
};
const SwapButton = ({
source,
target,
disabled,
showLoader,
onClick,
}: {
source: TokenInfo;
target: TokenInfo;
disabled: boolean;
showLoader: boolean;
onClick: () => void;
}) => {
const { isReady: isSourceWalletReady, walletAddress: sourceWalletAddress } =
useIsWalletReady(source.chainId, false);
const { isReady: isTargetWalletReady, walletAddress: targetWalletAddress } =
useIsWalletReady(target.chainId, false);
console.log(
"sourceWalletAddress",
sourceWalletAddress,
"targetWalletAddress",
targetWalletAddress
);
if (!isSourceWalletReady) {
return isEVMChain(source.chainId) ? (
<EthereumSignerKey />
) : source.chainId === CHAIN_ID_TERRA ? (
<TerraWalletKey />
) : null;
}
if (
!isTargetWalletReady &&
(!isEVMChain(source.chainId) || !isEVMChain(target.chainId))
) {
return isEVMChain(target.chainId) ? (
<EthereumSignerKey />
) : source.chainId === CHAIN_ID_TERRA ? (
<TerraWalletKey />
) : null;
}
return (
<ButtonWithLoader
disabled={disabled}
showLoader={showLoader}
onClick={onClick}
>
Swap
</ButtonWithLoader>
);
};
export default function Home() {
const classes = useStyles();
const [sourceTokenInfo, setSourceTokenInfo] = useState(MATIC_TOKEN_INFO);
@ -169,14 +248,16 @@ export default function Home() {
const [hasQuote, setHasQuote] = useState(false);
const { provider, signer } = useEthereumProvider();
const { enqueueSnackbar } = useSnackbar();
const [isFirstSwapComplete, setIsFirstSwapComplete] = useState(false);
const [isSecondSwapComplete, setIsSecondSwapComplete] = useState(false);
const [isSourceSwapComplete, setIsSourceSwapComplete] = useState(false);
const [isTargetSwapComplete, setIsTargetSwapComplete] = useState(false);
const [sourceTxBlockNumber, setSourceTxBlockNumber] = useState<
number | undefined
>(undefined);
const [hasSignedVAA, setHasSignedVAA] = useState(false);
const [relayerTimeoutString, setRelayerTimeoutString] = useState("");
const foo = useIsWalletReady(sourceTokenInfo.chainId);
const computeQuote = useCallback(() => {
(async () => {
setHasQuote(false);
@ -205,10 +286,15 @@ export default function Home() {
executor.setSlippage((parseFloat(slippage) / 100).toString());
executor.setRelayerFee(RELAYER_FEE_UST);
const quote = await executor.computeQuoteExactIn(amountIn);
// TODO: FIX
if (!quote || !quote.dst) {
throw new Error("failed to compute quote");
}
setExecutor(executor);
setAmountOut(
parseFloat(
executor.tokens.dstOut.formatAmount(quote.dst.minAmountOut)
// executor.tokens.dstOut.formatAmount(quote.dst.minAmountOut)
quote.minAmountOut
).toFixed(8)
);
setAmountInUST(
@ -260,30 +346,38 @@ export default function Home() {
setDeadline(deadline);
}, []);
const handleSourceChange = useCallback((event) => {
// NOTE: only native-to-native or wrapped-to-wrapped swaps are currently supported
if (event.target.value === WMATIC_TOKEN_INFO.name) {
setSourceTokenInfo(WMATIC_TOKEN_INFO);
setTargetTokenInfo(WETH_TOKEN_INFO);
} else if (event.target.value === WETH_TOKEN_INFO.name) {
setSourceTokenInfo(WETH_TOKEN_INFO);
setTargetTokenInfo(WMATIC_TOKEN_INFO);
} else if (event.target.value === ETH_TOKEN_INFO.name) {
setSourceTokenInfo(ETH_TOKEN_INFO);
setTargetTokenInfo(MATIC_TOKEN_INFO);
} else {
setSourceTokenInfo(MATIC_TOKEN_INFO);
setTargetTokenInfo(ETH_TOKEN_INFO);
const handleSourceChange = useCallback(
(event) => {
const tokenInfo = TOKEN_INFOS.find((x) => x.name === event.target.value);
if (tokenInfo) {
const supportedSwaps = getSupportedSwaps(tokenInfo);
console.log(supportedSwaps);
if (supportedSwaps) {
setSourceTokenInfo(tokenInfo);
if (!supportedSwaps.find((x) => x.name === targetTokenInfo.name)) {
setTargetTokenInfo(supportedSwaps[0]);
}
setAmountIn("");
setAmountOut("");
}
}
},
[targetTokenInfo]
);
const handleTargetChange = useCallback((event) => {
const tokenInfo = TOKEN_INFOS.find((x) => x.name === event.target.value);
if (tokenInfo) {
setTargetTokenInfo(tokenInfo);
setAmountOut("");
}
setAmountIn("");
setAmountOut("");
}, []);
const reset = useCallback(() => {
setIsSwapping(false);
setHasQuote(false);
setIsFirstSwapComplete(false);
setIsSecondSwapComplete(false);
setIsSourceSwapComplete(false);
setIsTargetSwapComplete(false);
setAmountIn("");
setAmountOut("");
setSourceTxBlockNumber(undefined);
@ -294,18 +388,19 @@ export default function Home() {
if (provider && signer && executor) {
try {
setIsSwapping(true);
setIsFirstSwapComplete(false);
setIsSourceSwapComplete(false);
setHasSignedVAA(false);
setIsSecondSwapComplete(false);
setIsTargetSwapComplete(false);
setRelayerTimeoutString("");
await switchProviderNetwork(provider, sourceTokenInfo.chainId);
await switchEvmProviderNetwork(provider, sourceTokenInfo.chainId);
const sourceReceipt = await executor.approveAndSwap(signer);
// TODO: fix
const sourceReceipt = await executor.evmApproveAndSwap(signer);
console.info(
"firstSwapTransactionHash:",
sourceReceipt.transactionHash
);
setIsFirstSwapComplete(true);
setIsSourceSwapComplete(true);
setSourceTxBlockNumber(sourceReceipt.blockNumber);
// Wait for the guardian network to reach consensus and emit the signedVAA
@ -319,7 +414,9 @@ export default function Home() {
// Check if the signedVAA has redeemed by the relayer
const isCompleted = await getIsTransferCompletedEvmWithRetry(
executor.dstExecutionParams.wormhole.tokenBridgeAddress,
executor.quoter.dstProvider,
// TODO: fix
//@ts-ignore
executor.quoter.getDstEvmProvider(),
vaaBytes,
// retry for two minutes
3000,
@ -330,14 +427,14 @@ export default function Home() {
setRelayerTimeoutString(
"Timed out waiting for relayer to complete swap. You'll need to complete it yourself."
);
await switchProviderNetwork(provider, targetTokenInfo.chainId);
await switchEvmProviderNetwork(provider, targetTokenInfo.chainId);
const targetReceipt = await executor.fetchVaaAndSwap(signer);
console.info(
"secondSwapTransactionHash:",
targetReceipt.transactionHash
);
}
setIsSecondSwapComplete(true);
setIsTargetSwapComplete(true);
} catch (e: any) {
reset();
console.error(e);
@ -357,6 +454,7 @@ export default function Home() {
]);
const readyToSwap = provider && signer && hasQuote;
const disableSelect = isSwapping || isComputingQuote;
return (
<div className={classes.bg}>
@ -367,9 +465,9 @@ export default function Home() {
</Typography>
<div className={classes.spacer} />
<Paper className={classes.mainPaper}>
<Collapse in={!isFirstSwapComplete}>
<Collapse in={!isSourceSwapComplete}>
<Settings
disabled={isSwapping || isComputingQuote}
disabled={disableSelect}
slippage={slippage}
deadline={deadline}
onSlippageChange={handleSlippageChange}
@ -379,13 +477,13 @@ export default function Home() {
tokens={TOKEN_INFOS}
value={sourceTokenInfo.name}
onChange={handleSourceChange}
disabled={isSwapping || isComputingQuote}
disabled={disableSelect}
></TokenSelect>
<Typography variant="subtitle1">Send</Typography>
<TextField
type="number"
value={amountIn}
disabled={isSwapping || isComputingQuote}
disabled={disableSelect}
InputProps={{ disableUnderline: true }}
className={classes.numberField}
onChange={handleAmountChange}
@ -397,12 +495,13 @@ export default function Home() {
color="error"
>{`The max input amount is ${sourceTokenInfo.maxAmount} ${sourceTokenInfo.name}`}</Typography>
) : null}
<ConnectedWalletAddress chainId={sourceTokenInfo.chainId} />
<div className={classes.spacer} />
<TokenSelect
tokens={TOKEN_INFOS}
tokens={getSupportedSwaps(sourceTokenInfo)}
value={targetTokenInfo.name}
onChange={() => {}}
disabled={true}
onChange={handleTargetChange}
disabled={disableSelect}
></TokenSelect>
<Typography variant="subtitle1">Receive (estimated)</Typography>
<TextField
@ -414,10 +513,20 @@ export default function Home() {
inputProps={{ readOnly: true }}
placeholder="0.0"
></TextField>
<ConnectedWalletAddress
chainId={
isEVMChain(sourceTokenInfo.chainId) &&
isEVMChain(targetTokenInfo.chainId)
? sourceTokenInfo.chainId
: targetTokenInfo.chainId
}
/>
<div className={classes.spacer} />
<Typography variant="subtitle2">{`Slippage tolerance: ${slippage}%`}</Typography>
<Typography variant="subtitle2">{`Relayer fee: ${RELAYER_FEE_UST} UST`}</Typography>
{!isSwapping && <EthereumSignerKey />}
<ButtonWithLoader
<SwapButton
source={sourceTokenInfo}
target={targetTokenInfo}
disabled={
!readyToSwap ||
isSwapping ||
@ -425,11 +534,9 @@ export default function Home() {
}
showLoader={isSwapping}
onClick={handleSwapClick}
>
Swap
</ButtonWithLoader>
/>
</Collapse>
<Collapse in={isFirstSwapComplete && !isSecondSwapComplete}>
<Collapse in={isSourceSwapComplete && !isTargetSwapComplete}>
<div className={classes.loaderHolder}>
<CircleLoader />
<div className={classes.spacer} />
@ -438,14 +545,14 @@ export default function Home() {
</Typography>
</div>
</Collapse>
<Collapse in={isSecondSwapComplete}>
<Collapse in={isTargetSwapComplete}>
<div className={classes.loaderHolder}>
<CheckCircleOutlineRounded
className={classes.successIcon}
fontSize={"inherit"}
/>
<Typography>Swap completed!</Typography>
<ButtonWithLoader onClick={() => reset()}>
<ButtonWithLoader onClick={reset}>
Swap more tokens!
</ButtonWithLoader>
</div>
@ -460,19 +567,23 @@ export default function Home() {
{`${amountOut} ${targetTokenInfo.name}`}
</Typography>
)}
{isFirstSwapComplete &&
!isSecondSwapComplete &&
{isSourceSwapComplete &&
!isTargetSwapComplete &&
!relayerTimeoutString && (
<SwapProgress
chainId={sourceTokenInfo.chainId}
txBlockNumber={sourceTxBlockNumber}
step={!hasSignedVAA ? 1 : !isSecondSwapComplete ? 2 : 3}
/>
<>
<SwapProgress
chainId={sourceTokenInfo.chainId}
txBlockNumber={sourceTxBlockNumber}
isSourceSwapComplete={isSourceSwapComplete}
hasSignedVAA={hasSignedVAA}
isTargetSwapComplete={isTargetSwapComplete}
/>
<div className={classes.spacer} />
</>
)}
{relayerTimeoutString && (
<Typography variant="subtitle1">{relayerTimeoutString}</Typography>
)}
<div className={classes.spacer} />
<Typography variant="subtitle2" color="error">
WARNING: this is a Testnet release only
</Typography>