diff --git a/bridge_ui/src/components/Attest/Create.tsx b/bridge_ui/src/components/Attest/Create.tsx
index 1e75f6ea4..70b66d4a7 100644
--- a/bridge_ui/src/components/Attest/Create.tsx
+++ b/bridge_ui/src/components/Attest/Create.tsx
@@ -1,16 +1,26 @@
+import { useSelector } from "react-redux";
import { useHandleCreateWrapped } from "../../hooks/useHandleCreateWrapped";
+import useIsWalletReady from "../../hooks/useIsWalletReady";
+import { selectAttestTargetChain } from "../../store/selectors";
import ButtonWithLoader from "../ButtonWithLoader";
+import KeyAndBalance from "../KeyAndBalance";
function Create() {
const { handleClick, disabled, showLoader } = useHandleCreateWrapped();
+ const targetChain = useSelector(selectAttestTargetChain);
+ const { isReady, statusMessage } = useIsWalletReady(targetChain);
return (
-
- Create
-
+ <>
+
+
+ Create
+
+ >
);
}
diff --git a/bridge_ui/src/components/Attest/Send.tsx b/bridge_ui/src/components/Attest/Send.tsx
index ff676c9c4..270285aae 100644
--- a/bridge_ui/src/components/Attest/Send.tsx
+++ b/bridge_ui/src/components/Attest/Send.tsx
@@ -1,16 +1,27 @@
+import { useSelector } from "react-redux";
import { useHandleAttest } from "../../hooks/useHandleAttest";
+import useIsWalletReady from "../../hooks/useIsWalletReady";
+import { selectAttestSourceChain } from "../../store/selectors";
import ButtonWithLoader from "../ButtonWithLoader";
+import KeyAndBalance from "../KeyAndBalance";
function Send() {
const { handleClick, disabled, showLoader } = useHandleAttest();
+ const sourceChain = useSelector(selectAttestSourceChain);
+ const { isReady, statusMessage } = useIsWalletReady(sourceChain);
+
return (
-
- Attest
-
+ <>
+
+
+ Attest
+
+ >
);
}
diff --git a/bridge_ui/src/components/Transfer/Redeem.tsx b/bridge_ui/src/components/Transfer/Redeem.tsx
index c70929d28..fd815d8ac 100644
--- a/bridge_ui/src/components/Transfer/Redeem.tsx
+++ b/bridge_ui/src/components/Transfer/Redeem.tsx
@@ -1,5 +1,6 @@
import { useSelector } from "react-redux";
import { useHandleRedeem } from "../../hooks/useHandleRedeem";
+import useIsWalletReady from "../../hooks/useIsWalletReady";
import { selectTransferTargetChain } from "../../store/selectors";
import ButtonWithLoader from "../ButtonWithLoader";
import KeyAndBalance from "../KeyAndBalance";
@@ -7,13 +8,15 @@ import KeyAndBalance from "../KeyAndBalance";
function Redeem() {
const { handleClick, disabled, showLoader } = useHandleRedeem();
const targetChain = useSelector(selectTransferTargetChain);
+ const { isReady, statusMessage } = useIsWalletReady(targetChain);
return (
<>
Redeem
diff --git a/bridge_ui/src/components/Transfer/Send.tsx b/bridge_ui/src/components/Transfer/Send.tsx
index 20dbe9ee4..50f08521b 100644
--- a/bridge_ui/src/components/Transfer/Send.tsx
+++ b/bridge_ui/src/components/Transfer/Send.tsx
@@ -1,5 +1,6 @@
import { useSelector } from "react-redux";
import { useHandleTransfer } from "../../hooks/useHandleTransfer";
+import useIsWalletReady from "../../hooks/useIsWalletReady";
import {
selectTransferSourceChain,
selectTransferTargetError,
@@ -11,14 +12,15 @@ function Send() {
const { handleClick, disabled, showLoader } = useHandleTransfer();
const sourceChain = useSelector(selectTransferSourceChain);
const error = useSelector(selectTransferTargetError);
+ const { isReady, statusMessage } = useIsWalletReady(sourceChain);
return (
<>
Transfer
diff --git a/bridge_ui/src/components/Transfer/Source.tsx b/bridge_ui/src/components/Transfer/Source.tsx
index 9006685c1..c36075dc2 100644
--- a/bridge_ui/src/components/Transfer/Source.tsx
+++ b/bridge_ui/src/components/Transfer/Source.tsx
@@ -35,7 +35,7 @@ function Source() {
const error = useSelector(selectTransferSourceError);
const isSourceComplete = useSelector(selectTransferIsSourceComplete);
const shouldLockFields = useSelector(selectTransferShouldLockFields);
- const isWalletReady = useIsWalletReady(sourceChain);
+ const { isReady, statusMessage } = useIsWalletReady(sourceChain);
const handleSourceChange = useCallback(
(event) => {
dispatch(setSourceChain(event.target.value));
@@ -67,7 +67,7 @@ function Source() {
))}
- {isWalletReady || uiAmountString ? (
+ {isReady || uiAmountString ? (
@@ -86,7 +86,7 @@ function Source() {
disabled={!isSourceComplete}
onClick={handleNextClick}
showLoader={false}
- error={error}
+ error={statusMessage || error}
>
Next
diff --git a/bridge_ui/src/components/Transfer/Target.tsx b/bridge_ui/src/components/Transfer/Target.tsx
index 3bc75b729..176445a91 100644
--- a/bridge_ui/src/components/Transfer/Target.tsx
+++ b/bridge_ui/src/components/Transfer/Target.tsx
@@ -2,6 +2,7 @@ import { CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { makeStyles, MenuItem, TextField } from "@material-ui/core";
import { useCallback, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
+import useIsWalletReady from "../../hooks/useIsWalletReady";
import useSyncTargetAddress from "../../hooks/useSyncTargetAddress";
import {
selectTransferIsTargetComplete,
@@ -43,6 +44,7 @@ function Target() {
const error = useSelector(selectTransferTargetError);
const isTargetComplete = useSelector(selectTransferIsTargetComplete);
const shouldLockFields = useSelector(selectTransferShouldLockFields);
+ const { statusMessage } = useIsWalletReady(targetChain);
useSyncTargetAddress(!shouldLockFields);
const handleTargetChange = useCallback(
(event) => {
@@ -93,7 +95,7 @@ function Target() {
disabled={!isTargetComplete}
onClick={handleNextClick}
showLoader={false}
- error={error}
+ error={statusMessage || error}
>
Next
diff --git a/bridge_ui/src/contexts/EthereumProviderContext.tsx b/bridge_ui/src/contexts/EthereumProviderContext.tsx
index 5904a479e..0d8fff522 100644
--- a/bridge_ui/src/contexts/EthereumProviderContext.tsx
+++ b/bridge_ui/src/contexts/EthereumProviderContext.tsx
@@ -1,5 +1,5 @@
import detectEthereumProvider from "@metamask/detect-provider";
-import { ethers } from "ethers";
+import { BigNumber, ethers } from "ethers";
import React, {
ReactChildren,
useCallback,
@@ -10,13 +10,12 @@ import React, {
type Provider = ethers.providers.Web3Provider | undefined;
type Signer = ethers.Signer | undefined;
-type Network = ethers.providers.Network | undefined;
interface IEthereumProviderContext {
connect(): void;
disconnect(): void;
provider: Provider;
- network: Network;
+ chainId: number | undefined;
signer: Signer;
signerAddress: string | undefined;
providerError: string | null;
@@ -26,7 +25,7 @@ const EthereumProviderContext = React.createContext({
connect: () => {},
disconnect: () => {},
provider: undefined,
- network: undefined,
+ chainId: undefined,
signer: undefined,
signerAddress: undefined,
providerError: null,
@@ -38,7 +37,7 @@ export const EthereumProviderProvider = ({
}) => {
const [providerError, setProviderError] = useState(null);
const [provider, setProvider] = useState(undefined);
- const [network, setNetwork] = useState(undefined);
+ const [chainId, setChainId] = useState(undefined);
const [signer, setSigner] = useState(undefined);
const [signerAddress, setSignerAddress] = useState(
undefined
@@ -51,7 +50,7 @@ export const EthereumProviderProvider = ({
const provider = new ethers.providers.Web3Provider(
// @ts-ignore
detectedProvider,
- "any" //TODO: should we only allow homestead? env perhaps?
+ "any"
);
provider
.send("eth_requestAccounts", [])
@@ -61,7 +60,7 @@ export const EthereumProviderProvider = ({
provider
.getNetwork()
.then((network) => {
- setNetwork(network);
+ setChainId(network.chainId);
})
.catch(() => {
setProviderError(
@@ -80,6 +79,16 @@ export const EthereumProviderProvider = ({
"An error occurred while getting the signer address"
);
});
+ // TODO: try using ethers directly
+ // @ts-ignore
+ if (detectedProvider && detectedProvider.on) {
+ // @ts-ignore
+ detectedProvider.on("chainChanged", (chainId) => {
+ try {
+ setChainId(BigNumber.from(chainId).toNumber());
+ } catch (e) {}
+ });
+ }
})
.catch(() => {
setProviderError(
@@ -97,7 +106,7 @@ export const EthereumProviderProvider = ({
const disconnect = useCallback(() => {
setProviderError(null);
setProvider(undefined);
- setNetwork(undefined);
+ setChainId(undefined);
setSigner(undefined);
setSignerAddress(undefined);
}, []);
@@ -107,7 +116,7 @@ export const EthereumProviderProvider = ({
connect,
disconnect,
provider,
- network,
+ chainId,
signer,
signerAddress,
providerError,
@@ -116,7 +125,7 @@ export const EthereumProviderProvider = ({
connect,
disconnect,
provider,
- network,
+ chainId,
signer,
signerAddress,
providerError,
diff --git a/bridge_ui/src/hooks/useIsWalletReady.ts b/bridge_ui/src/hooks/useIsWalletReady.ts
index 3ff78fef4..e2a28f131 100644
--- a/bridge_ui/src/hooks/useIsWalletReady.ts
+++ b/bridge_ui/src/hooks/useIsWalletReady.ts
@@ -5,26 +5,53 @@ import {
CHAIN_ID_TERRA,
} from "@certusone/wormhole-sdk";
import { useConnectedWallet } from "@terra-money/wallet-provider";
+import { useMemo } from "react";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
+import { ETH_NETWORK_CHAIN_ID } from "../utils/consts";
-function useIsWalletReady(chainId: ChainId) {
+const createWalletStatus = (isReady: boolean, statusMessage: string = "") => ({
+ isReady,
+ statusMessage,
+});
+
+function useIsWalletReady(chainId: ChainId): {
+ isReady: boolean;
+ statusMessage: string;
+} {
const solanaWallet = useSolanaWallet();
const solPK = solanaWallet?.publicKey;
const terraWallet = useConnectedWallet();
- const { provider, signerAddress } = useEthereumProvider();
+ const hasTerraWallet = !!terraWallet;
+ const {
+ provider,
+ signerAddress,
+ chainId: ethChainId,
+ } = useEthereumProvider();
+ const hasEthInfo = !!provider && !!signerAddress;
+ const hasCorrectEthNetwork = ethChainId === ETH_NETWORK_CHAIN_ID;
- let output = false;
- if (chainId === CHAIN_ID_TERRA && terraWallet) {
- output = true;
- } else if (chainId === CHAIN_ID_SOLANA && solPK) {
- output = true;
- } else if (chainId === CHAIN_ID_ETH && provider && signerAddress) {
- output = true;
- }
- //TODO bsc
-
- return output;
+ return useMemo(() => {
+ if (chainId === CHAIN_ID_TERRA && hasTerraWallet) {
+ // TODO: terraWallet does not update on wallet changes
+ return createWalletStatus(true);
+ }
+ if (chainId === CHAIN_ID_SOLANA && solPK) {
+ return createWalletStatus(true);
+ }
+ if (chainId === CHAIN_ID_ETH && hasEthInfo) {
+ if (hasCorrectEthNetwork) {
+ return createWalletStatus(true);
+ } else {
+ return createWalletStatus(
+ false,
+ `Wallet is not connected to ${process.env.REACT_APP_CLUSTER}. Expected Chain ID: ${ETH_NETWORK_CHAIN_ID}`
+ );
+ }
+ }
+ //TODO bsc
+ return createWalletStatus(false, "Wallet not connected");
+ }, [chainId, hasTerraWallet, solPK, hasEthInfo, hasCorrectEthNetwork]);
}
export default useIsWalletReady;
diff --git a/bridge_ui/src/utils/consts.ts b/bridge_ui/src/utils/consts.ts
index c112169e2..4dd9cd3a5 100644
--- a/bridge_ui/src/utils/consts.ts
+++ b/bridge_ui/src/utils/consts.ts
@@ -51,6 +51,12 @@ export const WORMHOLE_RPC_HOST =
process.env.REACT_APP_CLUSTER === "testnet"
? "https://wormhole-v2-testnet-api.certus.one"
: "http://localhost:8080";
+export const ETH_NETWORK_CHAIN_ID =
+ process.env.REACT_APP_CLUSTER === "mainnet"
+ ? 1
+ : process.env.REACT_APP_CLUSTER === "testnet"
+ ? 5
+ : 1337;
export const SOLANA_HOST =
process.env.REACT_APP_CLUSTER === "testnet"
? clusterApiUrl("testnet")
@@ -92,7 +98,8 @@ export const TERRA_TEST_TOKEN_ADDRESS =
export const TERRA_BRIDGE_ADDRESS =
"terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5";
export const TERRA_TOKEN_BRIDGE_ADDRESS =
- "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4";
+ "terra174kgn5rtw4kf6f938wm7kwh70h2v4vcfd26jlc";
+// "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4";
export const COVALENT_API_KEY = process.env.REACT_APP_COVALENT_API_KEY
? process.env.REACT_APP_COVALENT_API_KEY