bridge_ui: show warnings
fixes https://github.com/certusone/wormhole/issues/361 Change-Id: I69b357a56eaaf25d46c83ab5fd84bc05d3eaee2a
This commit is contained in:
parent
e70db48ef7
commit
49d41733a7
|
@ -1,18 +1,19 @@
|
||||||
import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
|
import { makeStyles, MenuItem, TextField } from "@material-ui/core";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import {
|
||||||
|
incrementStep,
|
||||||
|
setSourceAsset,
|
||||||
|
setSourceChain,
|
||||||
|
} from "../../store/attestSlice";
|
||||||
import {
|
import {
|
||||||
selectAttestIsSourceComplete,
|
selectAttestIsSourceComplete,
|
||||||
selectAttestShouldLockFields,
|
selectAttestShouldLockFields,
|
||||||
selectAttestSourceAsset,
|
selectAttestSourceAsset,
|
||||||
selectAttestSourceChain,
|
selectAttestSourceChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import {
|
|
||||||
incrementStep,
|
|
||||||
setSourceAsset,
|
|
||||||
setSourceChain,
|
|
||||||
} from "../../store/attestSlice";
|
|
||||||
import { CHAINS } from "../../utils/consts";
|
import { CHAINS } from "../../utils/consts";
|
||||||
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
@ -40,12 +41,9 @@ function Source() {
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
const handleNextClick = useCallback(
|
const handleNextClick = useCallback(() => {
|
||||||
(event) => {
|
dispatch(incrementStep());
|
||||||
dispatch(incrementStep());
|
}, [dispatch]);
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -70,14 +68,13 @@ function Source() {
|
||||||
onChange={handleAssetChange}
|
onChange={handleAssetChange}
|
||||||
disabled={shouldLockFields}
|
disabled={shouldLockFields}
|
||||||
/>
|
/>
|
||||||
<Button
|
<ButtonWithLoader
|
||||||
disabled={!isSourceComplete}
|
disabled={!isSourceComplete}
|
||||||
onClick={handleNextClick}
|
onClick={handleNextClick}
|
||||||
variant="contained"
|
showLoader={false}
|
||||||
color="primary"
|
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
</Button>
|
</ButtonWithLoader>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { Button, MenuItem, TextField } from "@material-ui/core";
|
import { MenuItem, TextField } from "@material-ui/core";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { incrementStep, setTargetChain } from "../../store/attestSlice";
|
||||||
import {
|
import {
|
||||||
selectAttestIsTargetComplete,
|
selectAttestIsTargetComplete,
|
||||||
selectAttestShouldLockFields,
|
selectAttestShouldLockFields,
|
||||||
selectAttestSourceChain,
|
selectAttestSourceChain,
|
||||||
selectAttestTargetChain,
|
selectAttestTargetChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { incrementStep, setTargetChain } from "../../store/attestSlice";
|
|
||||||
import { CHAINS } from "../../utils/consts";
|
import { CHAINS } from "../../utils/consts";
|
||||||
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
|
||||||
function Target() {
|
function Target() {
|
||||||
|
@ -27,12 +28,9 @@ function Target() {
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
const handleNextClick = useCallback(
|
const handleNextClick = useCallback(() => {
|
||||||
(event) => {
|
dispatch(incrementStep());
|
||||||
dispatch(incrementStep());
|
}, [dispatch]);
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -50,14 +48,13 @@ function Target() {
|
||||||
</TextField>
|
</TextField>
|
||||||
{/* TODO: determine "to" token address */}
|
{/* TODO: determine "to" token address */}
|
||||||
<KeyAndBalance chainId={targetChain} />
|
<KeyAndBalance chainId={targetChain} />
|
||||||
<Button
|
<ButtonWithLoader
|
||||||
disabled={!isTargetComplete}
|
disabled={!isTargetComplete}
|
||||||
onClick={handleNextClick}
|
onClick={handleNextClick}
|
||||||
variant="contained"
|
showLoader={false}
|
||||||
color="primary"
|
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
</Button>
|
</ButtonWithLoader>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import { Button, CircularProgress, makeStyles } from "@material-ui/core";
|
import {
|
||||||
|
Button,
|
||||||
|
CircularProgress,
|
||||||
|
makeStyles,
|
||||||
|
Typography,
|
||||||
|
} from "@material-ui/core";
|
||||||
import { ReactChild } from "react";
|
import { ReactChild } from "react";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
@ -17,38 +22,51 @@ const useStyles = makeStyles((theme) => ({
|
||||||
marginLeft: -12,
|
marginLeft: -12,
|
||||||
marginBottom: 6,
|
marginBottom: 6,
|
||||||
},
|
},
|
||||||
|
error: {
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function ButtonWithLoader({
|
export default function ButtonWithLoader({
|
||||||
disabled,
|
disabled,
|
||||||
onClick,
|
onClick,
|
||||||
showLoader,
|
showLoader,
|
||||||
|
error,
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
showLoader: boolean;
|
showLoader: boolean;
|
||||||
|
error?: string;
|
||||||
children: ReactChild;
|
children: ReactChild;
|
||||||
}) {
|
}) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<>
|
||||||
<Button
|
<div className={classes.root}>
|
||||||
color="primary"
|
<Button
|
||||||
variant="contained"
|
color="primary"
|
||||||
className={classes.button}
|
variant="contained"
|
||||||
disabled={disabled}
|
className={classes.button}
|
||||||
onClick={onClick}
|
disabled={disabled}
|
||||||
>
|
onClick={onClick}
|
||||||
{children}
|
>
|
||||||
</Button>
|
{children}
|
||||||
{showLoader ? (
|
</Button>
|
||||||
<CircularProgress
|
{showLoader ? (
|
||||||
size={24}
|
<CircularProgress
|
||||||
color="inherit"
|
size={24}
|
||||||
className={classes.loader}
|
color="inherit"
|
||||||
/>
|
className={classes.loader}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
{error ? (
|
||||||
|
<Typography color="error" className={classes.error}>
|
||||||
|
{error}
|
||||||
|
</Typography>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ const useStyles = makeStyles((theme) => ({
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
background: `radial-gradient(100% 100% at 100% 125%,${theme.palette.secondary.dark} 0,rgba(255,255,255,0) 100%)`,
|
background: `radial-gradient(100% 100% at 100% 125%,${theme.palette.secondary.dark} 0,rgba(255,255,255,0) 100%)`,
|
||||||
|
zIndex: -1,
|
||||||
},
|
},
|
||||||
hole: {
|
hole: {
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
|
@ -16,6 +17,7 @@ const useStyles = makeStyles((theme) => ({
|
||||||
right: 0,
|
right: 0,
|
||||||
opacity: 0.3,
|
opacity: 0.3,
|
||||||
filter: "blur(1px)",
|
filter: "blur(1px)",
|
||||||
|
zIndex: -1,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Toolbar } from "@material-ui/core";
|
import { makeStyles } from "@material-ui/core";
|
||||||
import DisconnectIcon from "@material-ui/icons/LinkOff";
|
import DisconnectIcon from "@material-ui/icons/LinkOff";
|
||||||
import {
|
import {
|
||||||
WalletDisconnectButton,
|
WalletDisconnectButton,
|
||||||
|
@ -6,18 +6,31 @@ import {
|
||||||
} from "@solana/wallet-adapter-material-ui";
|
} from "@solana/wallet-adapter-material-ui";
|
||||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
textAlign: "center",
|
||||||
|
margin: `${theme.spacing(1)}px auto`,
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: 400,
|
||||||
|
},
|
||||||
|
disconnectButton: {
|
||||||
|
marginLeft: theme.spacing(1),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const SolanaWalletKey = () => {
|
const SolanaWalletKey = () => {
|
||||||
|
const classes = useStyles();
|
||||||
const wallet = useSolanaWallet();
|
const wallet = useSolanaWallet();
|
||||||
return (
|
return (
|
||||||
<Toolbar style={{ display: "flex" }}>
|
<div className={classes.root}>
|
||||||
<WalletMultiButton />
|
<WalletMultiButton />
|
||||||
{wallet && (
|
{wallet && (
|
||||||
<WalletDisconnectButton
|
<WalletDisconnectButton
|
||||||
startIcon={<DisconnectIcon />}
|
startIcon={<DisconnectIcon />}
|
||||||
style={{ marginLeft: 8 }}
|
className={classes.disconnectButton}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Toolbar>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { useHandleTransfer } from "../../hooks/useHandleTransfer";
|
import { useHandleTransfer } from "../../hooks/useHandleTransfer";
|
||||||
import { selectTransferSourceChain } from "../../store/selectors";
|
import {
|
||||||
|
selectTransferSourceChain,
|
||||||
|
selectTransferTargetError,
|
||||||
|
} from "../../store/selectors";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
|
||||||
function Send() {
|
function Send() {
|
||||||
const { handleClick, disabled, showLoader } = useHandleTransfer();
|
const { handleClick, disabled, showLoader } = useHandleTransfer();
|
||||||
const sourceChain = useSelector(selectTransferSourceChain);
|
const sourceChain = useSelector(selectTransferSourceChain);
|
||||||
|
const error = useSelector(selectTransferTargetError);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<KeyAndBalance chainId={sourceChain} />
|
<KeyAndBalance chainId={sourceChain} />
|
||||||
|
@ -14,6 +18,7 @@ function Send() {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
showLoader={showLoader}
|
showLoader={showLoader}
|
||||||
|
error={error}
|
||||||
>
|
>
|
||||||
Transfer
|
Transfer
|
||||||
</ButtonWithLoader>
|
</ButtonWithLoader>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
|
import { makeStyles, MenuItem, TextField } from "@material-ui/core";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
||||||
|
@ -8,6 +8,7 @@ import {
|
||||||
selectTransferShouldLockFields,
|
selectTransferShouldLockFields,
|
||||||
selectTransferSourceBalanceString,
|
selectTransferSourceBalanceString,
|
||||||
selectTransferSourceChain,
|
selectTransferSourceChain,
|
||||||
|
selectTransferSourceError,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import {
|
import {
|
||||||
incrementStep,
|
incrementStep,
|
||||||
|
@ -15,6 +16,7 @@ import {
|
||||||
setSourceChain,
|
setSourceChain,
|
||||||
} from "../../store/transferSlice";
|
} from "../../store/transferSlice";
|
||||||
import { CHAINS } from "../../utils/consts";
|
import { CHAINS } from "../../utils/consts";
|
||||||
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
||||||
|
|
||||||
|
@ -30,6 +32,7 @@ function Source() {
|
||||||
const sourceChain = useSelector(selectTransferSourceChain);
|
const sourceChain = useSelector(selectTransferSourceChain);
|
||||||
const uiAmountString = useSelector(selectTransferSourceBalanceString);
|
const uiAmountString = useSelector(selectTransferSourceBalanceString);
|
||||||
const amount = useSelector(selectTransferAmount);
|
const amount = useSelector(selectTransferAmount);
|
||||||
|
const error = useSelector(selectTransferSourceError);
|
||||||
const isSourceComplete = useSelector(selectTransferIsSourceComplete);
|
const isSourceComplete = useSelector(selectTransferIsSourceComplete);
|
||||||
const shouldLockFields = useSelector(selectTransferShouldLockFields);
|
const shouldLockFields = useSelector(selectTransferShouldLockFields);
|
||||||
const isWalletReady = useIsWalletReady(sourceChain);
|
const isWalletReady = useIsWalletReady(sourceChain);
|
||||||
|
@ -45,12 +48,9 @@ function Source() {
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
const handleNextClick = useCallback(
|
const handleNextClick = useCallback(() => {
|
||||||
(event) => {
|
dispatch(incrementStep());
|
||||||
dispatch(incrementStep());
|
}, [dispatch]);
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -82,14 +82,14 @@ function Source() {
|
||||||
onChange={handleAmountChange}
|
onChange={handleAmountChange}
|
||||||
disabled={shouldLockFields}
|
disabled={shouldLockFields}
|
||||||
/>
|
/>
|
||||||
<Button
|
<ButtonWithLoader
|
||||||
disabled={!isSourceComplete}
|
disabled={!isSourceComplete}
|
||||||
onClick={handleNextClick}
|
onClick={handleNextClick}
|
||||||
variant="contained"
|
showLoader={false}
|
||||||
color="primary"
|
error={error}
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
</Button>
|
</ButtonWithLoader>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
|
import { CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
||||||
|
import { makeStyles, MenuItem, TextField } from "@material-ui/core";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
|
||||||
import useSyncTargetAddress from "../../hooks/useSyncTargetAddress";
|
import useSyncTargetAddress from "../../hooks/useSyncTargetAddress";
|
||||||
import {
|
import {
|
||||||
selectTransferIsTargetComplete,
|
selectTransferIsTargetComplete,
|
||||||
|
@ -11,10 +11,12 @@ import {
|
||||||
selectTransferTargetAsset,
|
selectTransferTargetAsset,
|
||||||
selectTransferTargetBalanceString,
|
selectTransferTargetBalanceString,
|
||||||
selectTransferTargetChain,
|
selectTransferTargetChain,
|
||||||
|
selectTransferTargetError,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { incrementStep, setTargetChain } from "../../store/transferSlice";
|
import { incrementStep, setTargetChain } from "../../store/transferSlice";
|
||||||
import { hexToNativeString } from "../../utils/array";
|
import { hexToNativeString } from "../../utils/array";
|
||||||
import { CHAINS } from "../../utils/consts";
|
import { CHAINS } from "../../utils/consts";
|
||||||
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
import SolanaCreateAssociatedAddress from "../SolanaCreateAssociatedAddress";
|
import SolanaCreateAssociatedAddress from "../SolanaCreateAssociatedAddress";
|
||||||
|
|
||||||
|
@ -33,11 +35,12 @@ function Target() {
|
||||||
[sourceChain]
|
[sourceChain]
|
||||||
);
|
);
|
||||||
const targetChain = useSelector(selectTransferTargetChain);
|
const targetChain = useSelector(selectTransferTargetChain);
|
||||||
const targetAddressHex = useSelector(selectTransferTargetAddressHex); // TODO: make readable
|
const targetAddressHex = useSelector(selectTransferTargetAddressHex);
|
||||||
const targetAsset = useSelector(selectTransferTargetAsset);
|
const targetAsset = useSelector(selectTransferTargetAsset);
|
||||||
const readableTargetAddress =
|
const readableTargetAddress =
|
||||||
hexToNativeString(targetAddressHex, targetChain) || "";
|
hexToNativeString(targetAddressHex, targetChain) || "";
|
||||||
const uiAmountString = useSelector(selectTransferTargetBalanceString);
|
const uiAmountString = useSelector(selectTransferTargetBalanceString);
|
||||||
|
const error = useSelector(selectTransferTargetError);
|
||||||
const isTargetComplete = useSelector(selectTransferIsTargetComplete);
|
const isTargetComplete = useSelector(selectTransferIsTargetComplete);
|
||||||
const shouldLockFields = useSelector(selectTransferShouldLockFields);
|
const shouldLockFields = useSelector(selectTransferShouldLockFields);
|
||||||
useSyncTargetAddress(!shouldLockFields);
|
useSyncTargetAddress(!shouldLockFields);
|
||||||
|
@ -47,12 +50,9 @@ function Target() {
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
const handleNextClick = useCallback(
|
const handleNextClick = useCallback(() => {
|
||||||
(event) => {
|
dispatch(incrementStep());
|
||||||
dispatch(incrementStep());
|
}, [dispatch]);
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -89,14 +89,14 @@ function Target() {
|
||||||
value={targetAsset || ""}
|
value={targetAsset || ""}
|
||||||
disabled={true}
|
disabled={true}
|
||||||
/>
|
/>
|
||||||
<Button
|
<ButtonWithLoader
|
||||||
disabled={!isTargetComplete}
|
disabled={!isTargetComplete}
|
||||||
onClick={handleNextClick}
|
onClick={handleNextClick}
|
||||||
variant="contained"
|
showLoader={false}
|
||||||
color="primary"
|
error={error}
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
</Button>
|
</ButtonWithLoader>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,6 @@ function useFetchTargetAsset() {
|
||||||
try {
|
try {
|
||||||
const asset = await getForeignAssetTerra(sourceChain, sourceAsset);
|
const asset = await getForeignAssetTerra(sourceChain, sourceAsset);
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
console.log("terra target asset", asset);
|
|
||||||
dispatch(setTargetAsset(asset));
|
dispatch(setTargetAsset(asset));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -59,19 +59,22 @@ function useSyncTargetAddress(shouldFire: boolean) {
|
||||||
} else if (targetChain === CHAIN_ID_SOLANA && solPK && targetAsset) {
|
} else if (targetChain === CHAIN_ID_SOLANA && solPK && targetAsset) {
|
||||||
// otherwise, use the associated token account (which we create in the case it doesn't exist)
|
// otherwise, use the associated token account (which we create in the case it doesn't exist)
|
||||||
(async () => {
|
(async () => {
|
||||||
const associatedTokenAccount = await Token.getAssociatedTokenAddress(
|
try {
|
||||||
ASSOCIATED_TOKEN_PROGRAM_ID,
|
const associatedTokenAccount =
|
||||||
TOKEN_PROGRAM_ID,
|
await Token.getAssociatedTokenAddress(
|
||||||
new PublicKey(targetAsset),
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||||
solPK
|
TOKEN_PROGRAM_ID,
|
||||||
);
|
new PublicKey(targetAsset), // this might error
|
||||||
if (!cancelled) {
|
solPK
|
||||||
dispatch(
|
);
|
||||||
setTargetAddressHex(
|
if (!cancelled) {
|
||||||
uint8ArrayToHex(zeroPad(associatedTokenAccount.toBytes(), 32))
|
dispatch(
|
||||||
)
|
setTargetAddressHex(
|
||||||
);
|
uint8ArrayToHex(zeroPad(associatedTokenAccount.toBytes(), 32))
|
||||||
}
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
})();
|
})();
|
||||||
} else if (
|
} else if (
|
||||||
targetChain === CHAIN_ID_TERRA &&
|
targetChain === CHAIN_ID_TERRA &&
|
||||||
|
|
|
@ -21,9 +21,6 @@ export const selectAttestIsSending = (state: RootState) =>
|
||||||
state.attest.isSending;
|
state.attest.isSending;
|
||||||
export const selectAttestIsCreating = (state: RootState) =>
|
export const selectAttestIsCreating = (state: RootState) =>
|
||||||
state.attest.isCreating;
|
state.attest.isCreating;
|
||||||
|
|
||||||
// safety checks
|
|
||||||
// TODO: could make this return a string with a user informative message
|
|
||||||
export const selectAttestIsSourceComplete = (state: RootState) =>
|
export const selectAttestIsSourceComplete = (state: RootState) =>
|
||||||
!!state.attest.sourceChain && !!state.attest.sourceAsset;
|
!!state.attest.sourceChain && !!state.attest.sourceAsset;
|
||||||
// TODO: check wrapped asset exists or is native attest
|
// TODO: check wrapped asset exists or is native attest
|
||||||
|
@ -74,49 +71,85 @@ export const selectTransferIsSending = (state: RootState) =>
|
||||||
state.transfer.isSending;
|
state.transfer.isSending;
|
||||||
export const selectTransferIsRedeeming = (state: RootState) =>
|
export const selectTransferIsRedeeming = (state: RootState) =>
|
||||||
state.transfer.isRedeeming;
|
state.transfer.isRedeeming;
|
||||||
|
export const selectTransferSourceError = (state: RootState) => {
|
||||||
// safety checks
|
if (!state.transfer.sourceChain) {
|
||||||
// TODO: could make this return a string with a user informative message
|
return "Select a source chain";
|
||||||
export const selectTransferIsSourceComplete = (state: RootState) => {
|
}
|
||||||
|
if (!state.transfer.sourceParsedTokenAccount) {
|
||||||
|
return "Select a token";
|
||||||
|
}
|
||||||
|
if (!state.transfer.amount) {
|
||||||
|
return "Enter an amount";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
state.transfer.sourceChain === CHAIN_ID_SOLANA &&
|
||||||
|
!state.transfer.sourceParsedTokenAccount.publicKey
|
||||||
|
) {
|
||||||
|
return "Token account unavailable";
|
||||||
|
}
|
||||||
|
if (!state.transfer.sourceParsedTokenAccount.uiAmountString) {
|
||||||
|
return "Token amount unavailable";
|
||||||
|
}
|
||||||
|
if (state.transfer.sourceParsedTokenAccount.decimals === 0) {
|
||||||
|
// TODO: more advanced NFT check
|
||||||
|
return "NFTs are not currently supported";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return (
|
// these may trigger error: fractional component exceeds decimals
|
||||||
!!state.transfer.sourceChain &&
|
if (
|
||||||
!!state.transfer.sourceParsedTokenAccount &&
|
|
||||||
!!state.transfer.amount &&
|
|
||||||
(state.transfer.sourceChain !== CHAIN_ID_SOLANA ||
|
|
||||||
!!state.transfer.sourceParsedTokenAccount.publicKey) &&
|
|
||||||
!!state.transfer.sourceParsedTokenAccount.uiAmountString &&
|
|
||||||
!!state.transfer.sourceParsedTokenAccount.decimals &&
|
|
||||||
state.transfer.sourceParsedTokenAccount.decimals > 0 && // TODO: more advanced NFT check
|
|
||||||
// may trigger error: fractional component exceeds decimals
|
|
||||||
parseUnits(
|
parseUnits(
|
||||||
state.transfer.amount,
|
state.transfer.amount,
|
||||||
state.transfer.sourceParsedTokenAccount.decimals
|
state.transfer.sourceParsedTokenAccount.decimals
|
||||||
).gt(0) &&
|
).lte(0)
|
||||||
// may trigger error: fractional component exceeds decimals
|
) {
|
||||||
|
return "Amount must be greater than zero";
|
||||||
|
}
|
||||||
|
if (
|
||||||
parseUnits(
|
parseUnits(
|
||||||
state.transfer.amount,
|
state.transfer.amount,
|
||||||
state.transfer.sourceParsedTokenAccount.decimals
|
state.transfer.sourceParsedTokenAccount.decimals
|
||||||
).lte(
|
).gt(
|
||||||
parseUnits(
|
parseUnits(
|
||||||
state.transfer.sourceParsedTokenAccount.uiAmountString,
|
state.transfer.sourceParsedTokenAccount.uiAmountString,
|
||||||
state.transfer.sourceParsedTokenAccount.decimals
|
state.transfer.sourceParsedTokenAccount.decimals
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
) {
|
||||||
|
return "Amount may not be greater than balance";
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
if (e?.message) {
|
||||||
|
return e.message.substring(0, e.message.indexOf("("));
|
||||||
|
}
|
||||||
|
return "Invalid amount";
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
export const selectTransferIsSourceComplete = (state: RootState) =>
|
||||||
|
!selectTransferSourceError(state);
|
||||||
|
export const selectTransferTargetError = (state: RootState) => {
|
||||||
|
const sourceError = selectTransferSourceError(state);
|
||||||
|
if (sourceError) {
|
||||||
|
return `Error in source: ${sourceError}`;
|
||||||
|
}
|
||||||
|
if (!state.transfer.targetChain) {
|
||||||
|
return "Select a target chain";
|
||||||
|
}
|
||||||
|
if (!state.transfer.targetAsset) {
|
||||||
|
return "Target asset unavailable. Is the token attested?";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
state.transfer.targetChain === CHAIN_ID_ETH &&
|
||||||
|
state.transfer.targetAsset === ethers.constants.AddressZero
|
||||||
|
) {
|
||||||
|
return "Target asset unavailable. Is the token attested?";
|
||||||
|
}
|
||||||
|
if (!state.transfer.targetAddressHex) {
|
||||||
|
return "Target account unavailable";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: check wrapped asset exists or is native transfer
|
|
||||||
export const selectTransferIsTargetComplete = (state: RootState) =>
|
export const selectTransferIsTargetComplete = (state: RootState) =>
|
||||||
selectTransferIsSourceComplete(state) &&
|
!selectTransferTargetError(state);
|
||||||
!!state.transfer.targetChain &&
|
|
||||||
!!state.transfer.targetAsset &&
|
|
||||||
(state.transfer.targetChain !== CHAIN_ID_ETH ||
|
|
||||||
state.transfer.targetAsset !== ethers.constants.AddressZero) &&
|
|
||||||
!!state.transfer.targetAddressHex;
|
|
||||||
export const selectTransferIsSendComplete = (state: RootState) =>
|
export const selectTransferIsSendComplete = (state: RootState) =>
|
||||||
!!selectTransferSignedVAAHex(state);
|
!!selectTransferSignedVAAHex(state);
|
||||||
export const selectTransferShouldLockFields = (state: RootState) =>
|
export const selectTransferShouldLockFields = (state: RootState) =>
|
||||||
|
|
Loading…
Reference in New Issue