bridge_ui: improve wallet checks
https: //github.com/certusone/wormhole/issues/360 Change-Id: I7ce3696aa0e038faea0da504aa9d8f4c69d7c6a6
This commit is contained in:
parent
e7a1dd600b
commit
e11e59095f
|
@ -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 (
|
||||
<ButtonWithLoader
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
showLoader={showLoader}
|
||||
>
|
||||
Create
|
||||
</ButtonWithLoader>
|
||||
<>
|
||||
<KeyAndBalance chainId={targetChain} />
|
||||
<ButtonWithLoader
|
||||
disabled={!isReady || disabled}
|
||||
onClick={handleClick}
|
||||
showLoader={showLoader}
|
||||
error={statusMessage}
|
||||
>
|
||||
Create
|
||||
</ButtonWithLoader>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 (
|
||||
<ButtonWithLoader
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
showLoader={showLoader}
|
||||
>
|
||||
Attest
|
||||
</ButtonWithLoader>
|
||||
<>
|
||||
<KeyAndBalance chainId={sourceChain} />
|
||||
<ButtonWithLoader
|
||||
disabled={!isReady || disabled}
|
||||
onClick={handleClick}
|
||||
showLoader={showLoader}
|
||||
error={statusMessage}
|
||||
>
|
||||
Attest
|
||||
</ButtonWithLoader>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
<KeyAndBalance chainId={targetChain} />
|
||||
<ButtonWithLoader
|
||||
disabled={disabled}
|
||||
disabled={!isReady || disabled}
|
||||
onClick={handleClick}
|
||||
showLoader={showLoader}
|
||||
error={statusMessage}
|
||||
>
|
||||
Redeem
|
||||
</ButtonWithLoader>
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
<KeyAndBalance chainId={sourceChain} />
|
||||
<ButtonWithLoader
|
||||
disabled={disabled}
|
||||
disabled={!isReady || disabled}
|
||||
onClick={handleClick}
|
||||
showLoader={showLoader}
|
||||
error={error}
|
||||
error={statusMessage || error}
|
||||
>
|
||||
Transfer
|
||||
</ButtonWithLoader>
|
||||
|
|
|
@ -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() {
|
|||
))}
|
||||
</TextField>
|
||||
<KeyAndBalance chainId={sourceChain} balance={uiAmountString} />
|
||||
{isWalletReady || uiAmountString ? (
|
||||
{isReady || uiAmountString ? (
|
||||
<div className={classes.transferField}>
|
||||
<TokenSelector disabled={shouldLockFields} />
|
||||
</div>
|
||||
|
@ -86,7 +86,7 @@ function Source() {
|
|||
disabled={!isSourceComplete}
|
||||
onClick={handleNextClick}
|
||||
showLoader={false}
|
||||
error={error}
|
||||
error={statusMessage || error}
|
||||
>
|
||||
Next
|
||||
</ButtonWithLoader>
|
||||
|
|
|
@ -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
|
||||
</ButtonWithLoader>
|
||||
|
|
|
@ -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<IEthereumProviderContext>({
|
|||
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<string | null>(null);
|
||||
const [provider, setProvider] = useState<Provider>(undefined);
|
||||
const [network, setNetwork] = useState<Network>(undefined);
|
||||
const [chainId, setChainId] = useState<number | undefined>(undefined);
|
||||
const [signer, setSigner] = useState<Signer>(undefined);
|
||||
const [signerAddress, setSignerAddress] = useState<string | undefined>(
|
||||
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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue