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

View File

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

View File

@ -16,17 +16,21 @@ const useStyles = makeStyles((theme) => ({
export default function TransactionProgress({ export default function TransactionProgress({
chainId, chainId,
txBlockNumber, txBlockNumber,
step, isSourceSwapComplete,
hasSignedVAA,
isTargetSwapComplete,
}: { }: {
chainId: ChainId; chainId: ChainId;
txBlockNumber: number | undefined; txBlockNumber: number | undefined;
step: number; isSourceSwapComplete: boolean;
hasSignedVAA: boolean;
isTargetSwapComplete: boolean;
}) { }) {
const classes = useStyles(); const classes = useStyles();
const { provider } = useEthereumProvider(); const { provider } = useEthereumProvider();
const [currentBlock, setCurrentBlock] = useState(0); const [currentBlock, setCurrentBlock] = useState(0);
useEffect(() => { useEffect(() => {
if (step !== 1 || !txBlockNumber) return; if (hasSignedVAA || !txBlockNumber) return;
if (isEVMChain(chainId) && provider) { if (isEVMChain(chainId) && provider) {
let cancelled = false; let cancelled = false;
(async () => { (async () => {
@ -46,7 +50,7 @@ export default function TransactionProgress({
cancelled = true; cancelled = true;
}; };
} }
}, [step, chainId, provider, txBlockNumber]); }, [hasSignedVAA, chainId, provider, txBlockNumber]);
const blockDiff = const blockDiff =
txBlockNumber !== undefined && txBlockNumber && currentBlock txBlockNumber !== undefined && txBlockNumber && currentBlock
? currentBlock - txBlockNumber ? currentBlock - txBlockNumber
@ -55,24 +59,20 @@ export default function TransactionProgress({
let value; let value;
let valueBuffer; let valueBuffer;
let message; let message;
switch (step) { if (!hasSignedVAA) {
case 1:
value = (blockDiff / expectedBlocks) * 50; value = (blockDiff / expectedBlocks) * 50;
valueBuffer = 50; valueBuffer = 50;
message = `Waiting for ${blockDiff} / ${expectedBlocks} confirmations on ${ message = `Waiting for ${blockDiff} / ${expectedBlocks} confirmations on ${
chainId === CHAIN_ID_POLYGON ? "Polygon" : "Ethereum" chainId === CHAIN_ID_POLYGON ? "Polygon" : "Ethereum"
}...`; }...`;
break; } else if (!isTargetSwapComplete) {
case 2:
value = 50; value = 50;
valueBuffer = 100; valueBuffer = 100;
message = "Waiting for relayer to complete swap..."; message = "Waiting for relayer to complete swap...";
break; } else {
case 3:
value = 100; value = 100;
valueBuffer = 100; valueBuffer = 100;
message = ""; message = "Success!";
break;
} }
return ( return (
<div className={classes.root}> <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 { hexlify, hexStripZeros } from "@ethersproject/bytes";
import { useConnectedWallet } from "@terra-money/wallet-provider";
import { useCallback, useMemo } from "react"; import { useCallback, useMemo } from "react";
import { useEthereumProvider } from "../contexts/EthereumProviderContext"; import { useEthereumProvider } from "../contexts/EthereumProviderContext";
// import { useSolanaWallet } from "../contexts/SolanaWalletContext"; // import { useSolanaWallet } from "../contexts/SolanaWalletContext";
@ -31,6 +37,8 @@ function useIsWalletReady(
const autoSwitch = enableNetworkAutoswitch; const autoSwitch = enableNetworkAutoswitch;
// const solanaWallet = useSolanaWallet(); // const solanaWallet = useSolanaWallet();
// const solPK = solanaWallet?.publicKey; // const solPK = solanaWallet?.publicKey;
const terraWallet = useConnectedWallet();
const hasTerraWallet = !!terraWallet;
const { const {
provider, provider,
signerAddress, signerAddress,
@ -54,6 +62,20 @@ function useIsWalletReady(
}, [provider, correctEvmNetwork, chainId]); }, [provider, correctEvmNetwork, chainId]);
return useMemo(() => { return useMemo(() => {
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) { //if (chainId === CHAIN_ID_SOLANA && solPK) {
// return createWalletStatus( // return createWalletStatus(
// true, // true,
@ -62,7 +84,6 @@ function useIsWalletReady(
// solPK.toString() // solPK.toString()
// ); // );
//} //}
if (isEVMChain(chainId) && hasEthInfo && signerAddress) {
if (hasCorrectEvmNetwork) { if (hasCorrectEvmNetwork) {
return createWalletStatus( return createWalletStatus(
true, true,
@ -93,12 +114,14 @@ function useIsWalletReady(
chainId, chainId,
autoSwitch, autoSwitch,
forceNetworkSwitch, forceNetworkSwitch,
hasTerraWallet,
// solPK, // solPK,
hasEthInfo, hasEthInfo,
correctEvmNetwork, correctEvmNetwork,
hasCorrectEvmNetwork, hasCorrectEvmNetwork,
provider, provider,
signerAddress, 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 App from "./App";
import ErrorBoundary from "./components/ErrorBoundary"; import ErrorBoundary from "./components/ErrorBoundary";
import { EthereumProviderProvider } from "./contexts/EthereumProviderContext"; import { EthereumProviderProvider } from "./contexts/EthereumProviderContext";
import { TerraWalletProvider } from "./contexts/TerraWalletContext";
import { theme } from "./muiTheme"; import { theme } from "./muiTheme";
ReactDOM.render( ReactDOM.render(
@ -12,9 +13,11 @@ ReactDOM.render(
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<CssBaseline> <CssBaseline>
<EthereumProviderProvider> <EthereumProviderProvider>
<TerraWalletProvider>
<SnackbarProvider maxSnack={3}> <SnackbarProvider maxSnack={3}>
<App /> <App />
</SnackbarProvider> </SnackbarProvider>
</TerraWalletProvider>
</EthereumProviderProvider> </EthereumProviderProvider>
</CssBaseline> </CssBaseline>
</ThemeProvider> </ThemeProvider>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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