bridge_ui: support multiple evm bridges
Change-Id: I3c416955e3e01707eec29404a483b1c223bffef4
This commit is contained in:
parent
659b7b2547
commit
4bdb714594
|
@ -1,4 +1,3 @@
|
||||||
import { CHAIN_ID_ETH } from "@certusone/wormhole-sdk";
|
|
||||||
import { makeStyles, MenuItem, TextField, Typography } from "@material-ui/core";
|
import { makeStyles, MenuItem, TextField, Typography } from "@material-ui/core";
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
|
@ -12,6 +11,7 @@ import {
|
||||||
selectAttestTargetChain,
|
selectAttestTargetChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
||||||
|
import { isEVMChain } from "../../utils/ethereum";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
import LowBalanceWarning from "../LowBalanceWarning";
|
import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
|
@ -64,8 +64,11 @@ function Target() {
|
||||||
You will have to pay transaction fees on{" "}
|
You will have to pay transaction fees on{" "}
|
||||||
{CHAINS_BY_ID[targetChain].name} to attest this token.{" "}
|
{CHAINS_BY_ID[targetChain].name} to attest this token.{" "}
|
||||||
</Typography>
|
</Typography>
|
||||||
{targetChain === CHAIN_ID_ETH && (
|
{isEVMChain(targetChain) && (
|
||||||
<EthGasEstimateSummary methodType="createWrapped" />
|
<EthGasEstimateSummary
|
||||||
|
methodType="createWrapped"
|
||||||
|
chainId={targetChain}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Alert>
|
</Alert>
|
||||||
<LowBalanceWarning chainId={targetChain} />
|
<LowBalanceWarning chainId={targetChain} />
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { Typography } from "@material-ui/core";
|
import { Typography } from "@material-ui/core";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
import EthereumSignerKey from "./EthereumSignerKey";
|
import EthereumSignerKey from "./EthereumSignerKey";
|
||||||
import SolanaWalletKey from "./SolanaWalletKey";
|
import SolanaWalletKey from "./SolanaWalletKey";
|
||||||
import TerraWalletKey from "./TerraWalletKey";
|
import TerraWalletKey from "./TerraWalletKey";
|
||||||
|
@ -17,7 +17,7 @@ function KeyAndBalance({
|
||||||
balance?: string;
|
balance?: string;
|
||||||
}) {
|
}) {
|
||||||
const balanceString = balance ? "Balance: " + balance : balance;
|
const balanceString = balance ? "Balance: " + balance : balance;
|
||||||
if (chainId === CHAIN_ID_ETH) {
|
if (isEVMChain(chainId)) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EthereumSignerKey />
|
<EthereumSignerKey />
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
import {
|
import { ChainId } from "@certusone/wormhole-sdk";
|
||||||
ChainId,
|
import { makeStyles, Typography } from "@material-ui/core";
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
|
||||||
} from "@certusone/wormhole-sdk";
|
|
||||||
import { Typography } from "@material-ui/core";
|
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
import { makeStyles } from "@material-ui/core";
|
|
||||||
import useTransactionFees from "../hooks/useTransactionFees";
|
|
||||||
import useIsWalletReady from "../hooks/useIsWalletReady";
|
import useIsWalletReady from "../hooks/useIsWalletReady";
|
||||||
|
import useTransactionFees from "../hooks/useTransactionFees";
|
||||||
|
import { getDefaultNativeCurrencySymbol } from "../utils/consts";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
alert: {
|
alert: {
|
||||||
|
@ -24,9 +20,9 @@ function LowBalanceWarning({ chainId }: { chainId: ChainId }) {
|
||||||
isReady &&
|
isReady &&
|
||||||
transactionFeeWarning.balanceString &&
|
transactionFeeWarning.balanceString &&
|
||||||
transactionFeeWarning.isSufficientBalance === false;
|
transactionFeeWarning.isSufficientBalance === false;
|
||||||
const warningMessage = `This wallet has a very low ${
|
const warningMessage = `This wallet has a very low ${getDefaultNativeCurrencySymbol(
|
||||||
chainId === CHAIN_ID_SOLANA ? "SOL" : chainId === CHAIN_ID_ETH ? "ETH" : ""
|
chainId
|
||||||
} balance and may not be able to pay for the upcoming transaction fees.`;
|
)} balance and may not be able to pay for the upcoming transaction fees.`;
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<Alert severity="warning" className={classes.alert}>
|
<Alert severity="warning" className={classes.alert}>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_BSC,
|
ChainId,
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
getEmitterAddressEth,
|
getEmitterAddressEth,
|
||||||
getEmitterAddressSolana,
|
getEmitterAddressSolana,
|
||||||
|
@ -41,13 +40,14 @@ import {
|
||||||
selectNFTSourceChain,
|
selectNFTSourceChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import {
|
import {
|
||||||
CHAINS,
|
CHAINS_WITH_NFT_SUPPORT,
|
||||||
ETH_BRIDGE_ADDRESS,
|
getBridgeAddressForChain,
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
getNFTBridgeAddressForChain,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_NFT_BRIDGE_ADDRESS,
|
SOL_NFT_BRIDGE_ADDRESS,
|
||||||
WORMHOLE_RPC_HOSTS,
|
WORMHOLE_RPC_HOSTS,
|
||||||
} from "../../utils/consts";
|
} from "../../utils/consts";
|
||||||
|
import { isEVMChain } from "../../utils/ethereum";
|
||||||
import { getSignedVAAWithRetry } from "../../utils/getSignedVAAWithRetry";
|
import { getSignedVAAWithRetry } from "../../utils/getSignedVAAWithRetry";
|
||||||
import parseError from "../../utils/parseError";
|
import parseError from "../../utils/parseError";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
@ -60,17 +60,23 @@ const useStyles = makeStyles((theme) => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
async function eth(
|
async function evm(
|
||||||
provider: ethers.providers.Web3Provider,
|
provider: ethers.providers.Web3Provider,
|
||||||
tx: string,
|
tx: string,
|
||||||
enqueueSnackbar: any
|
enqueueSnackbar: any,
|
||||||
|
chainId: ChainId
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const receipt = await provider.getTransactionReceipt(tx);
|
const receipt = await provider.getTransactionReceipt(tx);
|
||||||
const sequence = parseSequenceFromLogEth(receipt, ETH_BRIDGE_ADDRESS);
|
const sequence = parseSequenceFromLogEth(
|
||||||
const emitterAddress = getEmitterAddressEth(ETH_NFT_BRIDGE_ADDRESS);
|
receipt,
|
||||||
|
getBridgeAddressForChain(chainId)
|
||||||
|
);
|
||||||
|
const emitterAddress = getEmitterAddressEth(
|
||||||
|
getNFTBridgeAddressForChain(chainId)
|
||||||
|
);
|
||||||
const { vaaBytes } = await getSignedVAAWithRetry(
|
const { vaaBytes } = await getSignedVAAWithRetry(
|
||||||
CHAIN_ID_ETH,
|
chainId,
|
||||||
emitterAddress,
|
emitterAddress,
|
||||||
sequence.toString(),
|
sequence.toString(),
|
||||||
WORMHOLE_RPC_HOSTS.length
|
WORMHOLE_RPC_HOSTS.length
|
||||||
|
@ -137,14 +143,15 @@ function RecoveryDialogContent({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (recoverySourceTx) {
|
if (recoverySourceTx) {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
if (recoverySourceChain === CHAIN_ID_ETH && provider) {
|
if (isEVMChain(recoverySourceChain) && provider) {
|
||||||
setRecoverySourceTxError("");
|
setRecoverySourceTxError("");
|
||||||
setRecoverySourceTxIsLoading(true);
|
setRecoverySourceTxIsLoading(true);
|
||||||
(async () => {
|
(async () => {
|
||||||
const { vaa, error } = await eth(
|
const { vaa, error } = await evm(
|
||||||
provider,
|
provider,
|
||||||
recoverySourceTx,
|
recoverySourceTx,
|
||||||
enqueueSnackbar
|
enqueueSnackbar,
|
||||||
|
recoverySourceChain
|
||||||
);
|
);
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setRecoverySourceTxIsLoading(false);
|
setRecoverySourceTxIsLoading(false);
|
||||||
|
@ -259,16 +266,13 @@ function RecoveryDialogContent({
|
||||||
fullWidth
|
fullWidth
|
||||||
margin="normal"
|
margin="normal"
|
||||||
>
|
>
|
||||||
{CHAINS.filter(
|
{CHAINS_WITH_NFT_SUPPORT.map(({ id, name }) => (
|
||||||
({ id }) => id === CHAIN_ID_ETH || id === CHAIN_ID_SOLANA
|
|
||||||
).map(({ id, name }) => (
|
|
||||||
<MenuItem key={id} value={id}>
|
<MenuItem key={id} value={id}>
|
||||||
{name}
|
{name}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
{recoverySourceChain === CHAIN_ID_ETH ||
|
{isEVMChain(recoverySourceChain) ? (
|
||||||
recoverySourceChain === CHAIN_ID_BSC ? (
|
|
||||||
<KeyAndBalance chainId={recoverySourceChain} />
|
<KeyAndBalance chainId={recoverySourceChain} />
|
||||||
) : null}
|
) : null}
|
||||||
<TextField
|
<TextField
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
|
||||||
import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
|
import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
|
||||||
import { Restore, VerifiedUser } from "@material-ui/icons";
|
import { Restore, VerifiedUser } from "@material-ui/icons";
|
||||||
|
import { Alert } from "@material-ui/lab";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
||||||
|
import { incrementStep, setSourceChain } from "../../store/nftSlice";
|
||||||
import {
|
import {
|
||||||
selectNFTIsSourceComplete,
|
selectNFTIsSourceComplete,
|
||||||
selectNFTShouldLockFields,
|
selectNFTShouldLockFields,
|
||||||
|
@ -11,15 +13,13 @@ import {
|
||||||
selectNFTSourceChain,
|
selectNFTSourceChain,
|
||||||
selectNFTSourceError,
|
selectNFTSourceError,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { incrementStep, setSourceChain } from "../../store/nftSlice";
|
import { CHAINS_WITH_NFT_SUPPORT } from "../../utils/consts";
|
||||||
import { CHAINS } from "../../utils/consts";
|
import { isEVMChain } from "../../utils/ethereum";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
import StepDescription from "../StepDescription";
|
import StepDescription from "../StepDescription";
|
||||||
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
||||||
import { Alert } from "@material-ui/lab";
|
|
||||||
import LowBalanceWarning from "../LowBalanceWarning";
|
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
transferField: {
|
transferField: {
|
||||||
|
@ -94,15 +94,13 @@ function Source({
|
||||||
onChange={handleSourceChange}
|
onChange={handleSourceChange}
|
||||||
disabled={shouldLockFields}
|
disabled={shouldLockFields}
|
||||||
>
|
>
|
||||||
{CHAINS.filter(
|
{CHAINS_WITH_NFT_SUPPORT.map(({ id, name }) => (
|
||||||
({ id }) => id === CHAIN_ID_ETH || id === CHAIN_ID_SOLANA
|
|
||||||
).map(({ id, name }) => (
|
|
||||||
<MenuItem key={id} value={id}>
|
<MenuItem key={id} value={id}>
|
||||||
{name}
|
{name}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
{sourceChain === CHAIN_ID_ETH ? (
|
{isEVMChain(sourceChain) ? (
|
||||||
<Alert severity="info">
|
<Alert severity="info">
|
||||||
Only NFTs which implement ERC-721 are supported.
|
Only NFTs which implement ERC-721 are supported.
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
hexToNativeString,
|
hexToNativeString,
|
||||||
hexToUint8Array,
|
hexToUint8Array,
|
||||||
|
@ -27,7 +26,8 @@ import {
|
||||||
selectNFTTargetChain,
|
selectNFTTargetChain,
|
||||||
selectNFTTargetError,
|
selectNFTTargetError,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
import { CHAINS_BY_ID, CHAINS_WITH_NFT_SUPPORT } from "../../utils/consts";
|
||||||
|
import { isEVMChain } from "../../utils/ethereum";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
import LowBalanceWarning from "../LowBalanceWarning";
|
import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
|
@ -48,7 +48,7 @@ function Target() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const sourceChain = useSelector(selectNFTSourceChain);
|
const sourceChain = useSelector(selectNFTSourceChain);
|
||||||
const chains = useMemo(
|
const chains = useMemo(
|
||||||
() => CHAINS.filter((c) => c.id !== sourceChain),
|
() => CHAINS_WITH_NFT_SUPPORT.filter((c) => c.id !== sourceChain),
|
||||||
[sourceChain]
|
[sourceChain]
|
||||||
);
|
);
|
||||||
const targetChain = useSelector(selectNFTTargetChain);
|
const targetChain = useSelector(selectNFTTargetChain);
|
||||||
|
@ -93,11 +93,8 @@ function Target() {
|
||||||
fullWidth
|
fullWidth
|
||||||
value={targetChain}
|
value={targetChain}
|
||||||
onChange={handleTargetChange}
|
onChange={handleTargetChange}
|
||||||
disabled={true}
|
|
||||||
>
|
>
|
||||||
{chains
|
{chains.map(({ id, name }) => (
|
||||||
.filter(({ id }) => id === CHAIN_ID_ETH || id === CHAIN_ID_SOLANA)
|
|
||||||
.map(({ id, name }) => (
|
|
||||||
<MenuItem key={id} value={id}>
|
<MenuItem key={id} value={id}>
|
||||||
{name}
|
{name}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
@ -120,7 +117,7 @@ function Target() {
|
||||||
value={targetAsset || ""}
|
value={targetAsset || ""}
|
||||||
disabled={true}
|
disabled={true}
|
||||||
/>
|
/>
|
||||||
{targetChain === CHAIN_ID_ETH ? (
|
{isEVMChain(targetChain) ? (
|
||||||
<TextField
|
<TextField
|
||||||
label="TokenId"
|
label="TokenId"
|
||||||
fullWidth
|
fullWidth
|
||||||
|
@ -136,8 +133,8 @@ function Target() {
|
||||||
You will have to pay transaction fees on{" "}
|
You will have to pay transaction fees on{" "}
|
||||||
{CHAINS_BY_ID[targetChain].name} to redeem your NFT.
|
{CHAINS_BY_ID[targetChain].name} to redeem your NFT.
|
||||||
</Typography>
|
</Typography>
|
||||||
{targetChain === CHAIN_ID_ETH && (
|
{isEVMChain(targetChain) && (
|
||||||
<EthGasEstimateSummary methodType="nft" />
|
<EthGasEstimateSummary methodType="nft" chainId={targetChain} />
|
||||||
)}
|
)}
|
||||||
</Alert>
|
</Alert>
|
||||||
<LowBalanceWarning chainId={targetChain} />
|
<LowBalanceWarning chainId={targetChain} />
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
import { CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
||||||
import { makeStyles, Typography } from "@material-ui/core";
|
import { makeStyles, Typography } from "@material-ui/core";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
|
@ -9,6 +9,7 @@ import {
|
||||||
selectNFTTargetChain,
|
selectNFTTargetChain,
|
||||||
selectNFTTransferTx,
|
selectNFTTransferTx,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
|
import { isEVMChain } from "../../utils/ethereum";
|
||||||
import { WAITING_FOR_WALLET_AND_CONF } from "../Transfer/WaitingForWalletMessage";
|
import { WAITING_FOR_WALLET_AND_CONF } from "../Transfer/WaitingForWalletMessage";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
@ -33,7 +34,7 @@ export default function WaitingForWalletMessage() {
|
||||||
{WAITING_FOR_WALLET_AND_CONF}{" "}
|
{WAITING_FOR_WALLET_AND_CONF}{" "}
|
||||||
{targetChain === CHAIN_ID_SOLANA && isRedeeming
|
{targetChain === CHAIN_ID_SOLANA && isRedeeming
|
||||||
? "Note: there will be several transactions"
|
? "Note: there will be several transactions"
|
||||||
: sourceChain === CHAIN_ID_ETH && isSending
|
: isEVMChain(sourceChain) && isSending
|
||||||
? "Note: there will be two transactions"
|
? "Note: there will be two transactions"
|
||||||
: null}
|
: null}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
|
@ -30,15 +30,16 @@ import { getMetaplexData } from "../hooks/useMetaplexData";
|
||||||
import { COLORS } from "../muiTheme";
|
import { COLORS } from "../muiTheme";
|
||||||
import { NFTParsedTokenAccount } from "../store/nftSlice";
|
import { NFTParsedTokenAccount } from "../store/nftSlice";
|
||||||
import {
|
import {
|
||||||
CHAINS,
|
|
||||||
CHAINS_BY_ID,
|
CHAINS_BY_ID,
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
CHAINS_WITH_NFT_SUPPORT,
|
||||||
|
getNFTBridgeAddressForChain,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_NFT_BRIDGE_ADDRESS,
|
SOL_NFT_BRIDGE_ADDRESS,
|
||||||
} from "../utils/consts";
|
} from "../utils/consts";
|
||||||
import {
|
import {
|
||||||
ethNFTToNFTParsedTokenAccount,
|
ethNFTToNFTParsedTokenAccount,
|
||||||
getEthereumNFT,
|
getEthereumNFT,
|
||||||
|
isEVMChain,
|
||||||
isNFT,
|
isNFT,
|
||||||
isValidEthereumAddress,
|
isValidEthereumAddress,
|
||||||
} from "../utils/ethereum";
|
} from "../utils/ethereum";
|
||||||
|
@ -119,7 +120,7 @@ export default function NFTOriginVerifier() {
|
||||||
isReady &&
|
isReady &&
|
||||||
provider &&
|
provider &&
|
||||||
signerAddress &&
|
signerAddress &&
|
||||||
lookupChain === CHAIN_ID_ETH &&
|
isEVMChain(lookupChain) &&
|
||||||
lookupAsset &&
|
lookupAsset &&
|
||||||
lookupTokenId
|
lookupTokenId
|
||||||
) {
|
) {
|
||||||
|
@ -136,10 +137,11 @@ export default function NFTOriginVerifier() {
|
||||||
signerAddress
|
signerAddress
|
||||||
);
|
);
|
||||||
const info = await getOriginalAssetEth(
|
const info = await getOriginalAssetEth(
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
getNFTBridgeAddressForChain(lookupChain),
|
||||||
provider,
|
provider,
|
||||||
lookupAsset,
|
lookupAsset,
|
||||||
lookupTokenId
|
lookupTokenId,
|
||||||
|
lookupChain
|
||||||
);
|
);
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
@ -225,7 +227,7 @@ export default function NFTOriginVerifier() {
|
||||||
originInfo.chainId
|
originInfo.chainId
|
||||||
);
|
);
|
||||||
const displayError =
|
const displayError =
|
||||||
(lookupChain === CHAIN_ID_ETH && statusMessage) || lookupError;
|
(isEVMChain(lookupChain) && statusMessage) || lookupError;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Container maxWidth="md">
|
<Container maxWidth="md">
|
||||||
|
@ -249,15 +251,13 @@ export default function NFTOriginVerifier() {
|
||||||
fullWidth
|
fullWidth
|
||||||
margin="normal"
|
margin="normal"
|
||||||
>
|
>
|
||||||
{CHAINS.filter(
|
{CHAINS_WITH_NFT_SUPPORT.map(({ id, name }) => (
|
||||||
({ id }) => id === CHAIN_ID_ETH || id === CHAIN_ID_SOLANA
|
|
||||||
).map(({ id, name }) => (
|
|
||||||
<MenuItem key={id} value={id}>
|
<MenuItem key={id} value={id}>
|
||||||
{name}
|
{name}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
{lookupChain === CHAIN_ID_ETH || lookupChain === CHAIN_ID_BSC ? (
|
{isEVMChain(lookupChain) ? (
|
||||||
<KeyAndBalance chainId={lookupChain} />
|
<KeyAndBalance chainId={lookupChain} />
|
||||||
) : null}
|
) : null}
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -267,7 +267,7 @@ export default function NFTOriginVerifier() {
|
||||||
value={lookupAsset}
|
value={lookupAsset}
|
||||||
onChange={handleAssetChange}
|
onChange={handleAssetChange}
|
||||||
/>
|
/>
|
||||||
{lookupChain === CHAIN_ID_ETH ? (
|
{isEVMChain(lookupChain) ? (
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
margin="normal"
|
margin="normal"
|
||||||
|
@ -318,6 +318,16 @@ export default function NFTOriginVerifier() {
|
||||||
>
|
>
|
||||||
View on Solscan
|
View on Solscan
|
||||||
</Button>
|
</Button>
|
||||||
|
) : originInfo.chainId === CHAIN_ID_BSC ? (
|
||||||
|
<Button
|
||||||
|
href={`https://bscscan.com/token/${readableAddress}?a=${originInfo.tokenId}`}
|
||||||
|
target="_blank"
|
||||||
|
endIcon={<Launch />}
|
||||||
|
className={classes.viewButton}
|
||||||
|
variant="outlined"
|
||||||
|
>
|
||||||
|
View on BscScan
|
||||||
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
href={`https://opensea.io/assets/${readableAddress}/${originInfo.tokenId}`}
|
href={`https://opensea.io/assets/${readableAddress}/${originInfo.tokenId}`}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
|
CHAIN_ID_BSC,
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { Button, makeStyles, Typography } from "@material-ui/core";
|
import { Button, makeStyles, Typography } from "@material-ui/core";
|
||||||
import { Transaction } from "../store/transferSlice";
|
import { Transaction } from "../store/transferSlice";
|
||||||
import { CLUSTER } from "../utils/consts";
|
import { CLUSTER, getExplorerName } from "../utils/consts";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
tx: {
|
tx: {
|
||||||
|
@ -36,6 +37,8 @@ export default function ShowTx({
|
||||||
? `https://${CLUSTER === "testnet" ? "goerli." : ""}etherscan.io/tx/${
|
? `https://${CLUSTER === "testnet" ? "goerli." : ""}etherscan.io/tx/${
|
||||||
tx?.id
|
tx?.id
|
||||||
}`
|
}`
|
||||||
|
: chainId === CHAIN_ID_BSC
|
||||||
|
? `https://bscscan.com/tx/${tx?.id}`
|
||||||
: chainId === CHAIN_ID_SOLANA
|
: chainId === CHAIN_ID_SOLANA
|
||||||
? `https://explorer.solana.com/tx/${tx?.id}${
|
? `https://explorer.solana.com/tx/${tx?.id}${
|
||||||
CLUSTER === "testnet"
|
CLUSTER === "testnet"
|
||||||
|
@ -53,12 +56,7 @@ export default function ShowTx({
|
||||||
: "columbus-5"
|
: "columbus-5"
|
||||||
}/tx/${tx?.id}`
|
}/tx/${tx?.id}`
|
||||||
: undefined;
|
: undefined;
|
||||||
const explorerName =
|
const explorerName = getExplorerName(chainId);
|
||||||
chainId === CHAIN_ID_ETH
|
|
||||||
? "Etherscan"
|
|
||||||
: chainId === CHAIN_ID_TERRA
|
|
||||||
? "Finder"
|
|
||||||
: "Explorer";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.tx}>
|
<div className={classes.tx}>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
|
CHAIN_ID_BSC,
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
|
@ -10,7 +11,7 @@ import { withStyles } from "@material-ui/styles";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import useCopyToClipboard from "../hooks/useCopyToClipboard";
|
import useCopyToClipboard from "../hooks/useCopyToClipboard";
|
||||||
import { ParsedTokenAccount } from "../store/transferSlice";
|
import { ParsedTokenAccount } from "../store/transferSlice";
|
||||||
import { CLUSTER } from "../utils/consts";
|
import { CLUSTER, getExplorerName } from "../utils/consts";
|
||||||
import { shortenAddress } from "../utils/solana";
|
import { shortenAddress } from "../utils/solana";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
@ -86,6 +87,8 @@ export default function SmartAddress({
|
||||||
? `https://${
|
? `https://${
|
||||||
CLUSTER === "testnet" ? "goerli." : ""
|
CLUSTER === "testnet" ? "goerli." : ""
|
||||||
}etherscan.io/address/${useableAddress}`
|
}etherscan.io/address/${useableAddress}`
|
||||||
|
: chainId === CHAIN_ID_BSC
|
||||||
|
? `https://bscscan.com/address/${useableAddress}`
|
||||||
: chainId === CHAIN_ID_SOLANA
|
: chainId === CHAIN_ID_SOLANA
|
||||||
? `https://explorer.solana.com/address/${useableAddress}${
|
? `https://explorer.solana.com/address/${useableAddress}${
|
||||||
CLUSTER === "testnet"
|
CLUSTER === "testnet"
|
||||||
|
@ -103,12 +106,7 @@ export default function SmartAddress({
|
||||||
: "columbus-5"
|
: "columbus-5"
|
||||||
}/address/${useableAddress}`
|
}/address/${useableAddress}`
|
||||||
: undefined;
|
: undefined;
|
||||||
const explorerName =
|
const explorerName = getExplorerName(chainId);
|
||||||
chainId === CHAIN_ID_ETH
|
|
||||||
? "Etherscan"
|
|
||||||
: chainId === CHAIN_ID_TERRA
|
|
||||||
? "Finder"
|
|
||||||
: "Explorer";
|
|
||||||
|
|
||||||
const copyToClipboard = useCopyToClipboard(useableAddress);
|
const copyToClipboard = useCopyToClipboard(useableAddress);
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ import { NFTParsedTokenAccount } from "../../store/nftSlice";
|
||||||
import NFTViewer from "./NFTViewer";
|
import NFTViewer from "./NFTViewer";
|
||||||
import { useDebounce } from "use-debounce/lib";
|
import { useDebounce } from "use-debounce/lib";
|
||||||
import RefreshButtonWrapper from "./RefreshButtonWrapper";
|
import RefreshButtonWrapper from "./RefreshButtonWrapper";
|
||||||
import { CHAIN_ID_ETH } from "@certusone/wormhole-sdk";
|
import { ChainId, CHAIN_ID_ETH } from "@certusone/wormhole-sdk";
|
||||||
import { sortParsedTokenAccounts } from "../../utils/sort";
|
import { sortParsedTokenAccounts } from "../../utils/sort";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) =>
|
const useStyles = makeStyles((theme) =>
|
||||||
|
@ -83,7 +83,10 @@ const getLogo = (account: ParsedTokenAccount | null) => {
|
||||||
return account.logo;
|
return account.logo;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isWormholev1 = (provider: any, address: string) => {
|
const isWormholev1 = (provider: any, address: string, chainId: ChainId) => {
|
||||||
|
if (chainId !== CHAIN_ID_ETH) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
const connection = WormholeAbi__factory.connect(
|
const connection = WormholeAbi__factory.connect(
|
||||||
WORMHOLE_V1_ETH_ADDRESS,
|
WORMHOLE_V1_ETH_ADDRESS,
|
||||||
provider
|
provider
|
||||||
|
@ -102,6 +105,7 @@ type EthereumSourceTokenSelectorProps = {
|
||||||
tokenAccounts: DataWrapper<ParsedTokenAccount[]> | undefined;
|
tokenAccounts: DataWrapper<ParsedTokenAccount[]> | undefined;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
resetAccounts: (() => void) | undefined;
|
resetAccounts: (() => void) | undefined;
|
||||||
|
chainId: ChainId;
|
||||||
nft?: boolean;
|
nft?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -186,6 +190,7 @@ export default function EthereumSourceTokenSelector(
|
||||||
tokenAccounts,
|
tokenAccounts,
|
||||||
disabled,
|
disabled,
|
||||||
resetAccounts,
|
resetAccounts,
|
||||||
|
chainId,
|
||||||
nft,
|
nft,
|
||||||
} = props;
|
} = props;
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
@ -258,7 +263,7 @@ export default function EthereumSourceTokenSelector(
|
||||||
onChange(autocompleteHolder);
|
onChange(autocompleteHolder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isWormholev1(provider, autocompleteHolder.mintKey).then(
|
isWormholev1(provider, autocompleteHolder.mintKey, chainId).then(
|
||||||
(result) => {
|
(result) => {
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
result
|
result
|
||||||
|
@ -282,7 +287,7 @@ export default function EthereumSourceTokenSelector(
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [autocompleteHolder, provider, advancedMode, onChange, nft]);
|
}, [autocompleteHolder, provider, advancedMode, onChange, nft, chainId]);
|
||||||
|
|
||||||
//This effect watches the advancedModeString, and checks that the selected asset is valid before putting
|
//This effect watches the advancedModeString, and checks that the selected asset is valid before putting
|
||||||
// it on the state.
|
// it on the state.
|
||||||
|
@ -353,7 +358,8 @@ export default function EthereumSourceTokenSelector(
|
||||||
//Validate that the token is not a wormhole v1 asset
|
//Validate that the token is not a wormhole v1 asset
|
||||||
const isWormholePromise = isWormholev1(
|
const isWormholePromise = isWormholev1(
|
||||||
provider,
|
provider,
|
||||||
advancedModeHolderString
|
advancedModeHolderString,
|
||||||
|
chainId
|
||||||
).then(
|
).then(
|
||||||
(result) => {
|
(result) => {
|
||||||
if (result && !cancelled) {
|
if (result && !cancelled) {
|
||||||
|
@ -430,6 +436,7 @@ export default function EthereumSourceTokenSelector(
|
||||||
onChange,
|
onChange,
|
||||||
nft,
|
nft,
|
||||||
advancedModeHolderTokenId,
|
advancedModeHolderTokenId,
|
||||||
|
chainId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
|
@ -563,7 +570,7 @@ export default function EthereumSourceTokenSelector(
|
||||||
const content = value ? (
|
const content = value ? (
|
||||||
<>
|
<>
|
||||||
{nft ? (
|
{nft ? (
|
||||||
<NFTViewer value={value} chainId={CHAIN_ID_ETH} />
|
<NFTViewer value={value} chainId={chainId} />
|
||||||
) : (
|
) : (
|
||||||
<RefreshButtonWrapper callback={resetAccountWrapper}>
|
<RefreshButtonWrapper callback={resetAccountWrapper}>
|
||||||
<Typography>
|
<Typography>
|
||||||
|
|
|
@ -13,10 +13,12 @@ import { NFTParsedTokenAccount } from "../../store/nftSlice";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
|
CHAIN_ID_BSC,
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import SmartAddress from "../SmartAddress";
|
import SmartAddress from "../SmartAddress";
|
||||||
|
import bscIcon from "../../icons/bsc.svg";
|
||||||
import ethIcon from "../../icons/eth.svg";
|
import ethIcon from "../../icons/eth.svg";
|
||||||
import solanaIcon from "../../icons/solana.svg";
|
import solanaIcon from "../../icons/solana.svg";
|
||||||
import useCopyToClipboard from "../../hooks/useCopyToClipboard";
|
import useCopyToClipboard from "../../hooks/useCopyToClipboard";
|
||||||
|
@ -53,6 +55,18 @@ const LogoIcon = ({ chainId }: { chainId: ChainId }) =>
|
||||||
src={ethIcon}
|
src={ethIcon}
|
||||||
alt="Ethereum"
|
alt="Ethereum"
|
||||||
/>
|
/>
|
||||||
|
) : chainId === CHAIN_ID_BSC ? (
|
||||||
|
<Avatar
|
||||||
|
style={{
|
||||||
|
backgroundColor: "rgb(20, 21, 26)",
|
||||||
|
height: "1em",
|
||||||
|
width: "1em",
|
||||||
|
marginLeft: "4px",
|
||||||
|
padding: "2px",
|
||||||
|
}}
|
||||||
|
src={bscIcon}
|
||||||
|
alt="Binance Smart Chain"
|
||||||
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
@ -127,6 +141,12 @@ const useStyles = makeStyles((theme) => ({
|
||||||
background:
|
background:
|
||||||
"linear-gradient(160deg, rgba(69,74,117,1) 0%, rgba(138,146,178,1) 33%, rgba(69,74,117,1) 66%, rgba(98,104,143,1) 100%)",
|
"linear-gradient(160deg, rgba(69,74,117,1) 0%, rgba(138,146,178,1) 33%, rgba(69,74,117,1) 66%, rgba(98,104,143,1) 100%)",
|
||||||
},
|
},
|
||||||
|
bsc: {
|
||||||
|
// color from binance background rgb(20, 21, 26), 2 and 1 tint lighter
|
||||||
|
backgroundColor: "#F0B90B",
|
||||||
|
background:
|
||||||
|
"linear-gradient(160deg, rgb(20, 21, 26) 0%, #4A4D57 33%, rgb(20, 21, 26) 66%, #2C2F3B 100%)",
|
||||||
|
},
|
||||||
solana: {
|
solana: {
|
||||||
// colors from https://solana.com/branding/new/exchange/exchange-sq-black.svg
|
// colors from https://solana.com/branding/new/exchange/exchange-sq-black.svg
|
||||||
backgroundColor: "rgb(153,69,255)",
|
backgroundColor: "rgb(153,69,255)",
|
||||||
|
@ -211,6 +231,7 @@ export default function NFTViewer({
|
||||||
<div
|
<div
|
||||||
className={clsx(classes.cardInset, {
|
className={clsx(classes.cardInset, {
|
||||||
[classes.eth]: chainId === CHAIN_ID_ETH,
|
[classes.eth]: chainId === CHAIN_ID_ETH,
|
||||||
|
[classes.bsc]: chainId === CHAIN_ID_BSC,
|
||||||
[classes.solana]: chainId === CHAIN_ID_SOLANA,
|
[classes.solana]: chainId === CHAIN_ID_SOLANA,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
//import Autocomplete from '@material-ui/lab/Autocomplete';
|
//import Autocomplete from '@material-ui/lab/Autocomplete';
|
||||||
import {
|
import { CHAIN_ID_SOLANA, CHAIN_ID_TERRA } from "@certusone/wormhole-sdk";
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
|
||||||
CHAIN_ID_TERRA,
|
|
||||||
} from "@certusone/wormhole-sdk";
|
|
||||||
import { TextField, Typography } from "@material-ui/core";
|
import { TextField, Typography } 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 useGetSourceParsedTokens from "../../hooks/useGetSourceParsedTokenAccounts";
|
import useGetSourceParsedTokens from "../../hooks/useGetSourceParsedTokenAccounts";
|
||||||
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
||||||
|
import {
|
||||||
|
setSourceParsedTokenAccount as setNFTSourceParsedTokenAccount,
|
||||||
|
setSourceWalletAddress as setNFTSourceWalletAddress,
|
||||||
|
} from "../../store/nftSlice";
|
||||||
import {
|
import {
|
||||||
selectNFTSourceChain,
|
selectNFTSourceChain,
|
||||||
selectNFTSourceParsedTokenAccount,
|
selectNFTSourceParsedTokenAccount,
|
||||||
|
@ -20,14 +20,11 @@ import {
|
||||||
setSourceParsedTokenAccount as setTransferSourceParsedTokenAccount,
|
setSourceParsedTokenAccount as setTransferSourceParsedTokenAccount,
|
||||||
setSourceWalletAddress as setTransferSourceWalletAddress,
|
setSourceWalletAddress as setTransferSourceWalletAddress,
|
||||||
} from "../../store/transferSlice";
|
} from "../../store/transferSlice";
|
||||||
import {
|
import { isEVMChain } from "../../utils/ethereum";
|
||||||
setSourceParsedTokenAccount as setNFTSourceParsedTokenAccount,
|
|
||||||
setSourceWalletAddress as setNFTSourceWalletAddress,
|
|
||||||
} from "../../store/nftSlice";
|
|
||||||
import EthereumSourceTokenSelector from "./EthereumSourceTokenSelector";
|
import EthereumSourceTokenSelector from "./EthereumSourceTokenSelector";
|
||||||
|
import RefreshButtonWrapper from "./RefreshButtonWrapper";
|
||||||
import SolanaSourceTokenSelector from "./SolanaSourceTokenSelector";
|
import SolanaSourceTokenSelector from "./SolanaSourceTokenSelector";
|
||||||
import TerraSourceTokenSelector from "./TerraSourceTokenSelector";
|
import TerraSourceTokenSelector from "./TerraSourceTokenSelector";
|
||||||
import RefreshButtonWrapper from "./RefreshButtonWrapper";
|
|
||||||
|
|
||||||
type TokenSelectorProps = {
|
type TokenSelectorProps = {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
@ -78,7 +75,7 @@ export const TokenSelector = (props: TokenSelectorProps) => {
|
||||||
|
|
||||||
//This is only for errors so bad that we shouldn't even mount the component
|
//This is only for errors so bad that we shouldn't even mount the component
|
||||||
const fatalError =
|
const fatalError =
|
||||||
lookupChain !== CHAIN_ID_ETH &&
|
isEVMChain(lookupChain) &&
|
||||||
lookupChain !== CHAIN_ID_TERRA &&
|
lookupChain !== CHAIN_ID_TERRA &&
|
||||||
maps?.tokenAccounts?.error; //Terra & ETH can proceed because it has advanced mode
|
maps?.tokenAccounts?.error; //Terra & ETH can proceed because it has advanced mode
|
||||||
|
|
||||||
|
@ -96,7 +93,7 @@ export const TokenSelector = (props: TokenSelectorProps) => {
|
||||||
resetAccounts={maps?.resetAccounts}
|
resetAccounts={maps?.resetAccounts}
|
||||||
nft={nft}
|
nft={nft}
|
||||||
/>
|
/>
|
||||||
) : lookupChain === CHAIN_ID_ETH ? (
|
) : isEVMChain(lookupChain) ? (
|
||||||
<EthereumSourceTokenSelector
|
<EthereumSourceTokenSelector
|
||||||
value={sourceParsedTokenAccount || null}
|
value={sourceParsedTokenAccount || null}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -104,6 +101,7 @@ export const TokenSelector = (props: TokenSelectorProps) => {
|
||||||
covalent={maps?.covalent || undefined}
|
covalent={maps?.covalent || undefined}
|
||||||
tokenAccounts={maps?.tokenAccounts}
|
tokenAccounts={maps?.tokenAccounts}
|
||||||
resetAccounts={maps?.resetAccounts}
|
resetAccounts={maps?.resetAccounts}
|
||||||
|
chainId={lookupChain}
|
||||||
nft={nft}
|
nft={nft}
|
||||||
/>
|
/>
|
||||||
) : lookupChain === CHAIN_ID_TERRA ? (
|
) : lookupChain === CHAIN_ID_TERRA ? (
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { useEffect, useState } from "react";
|
||||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
import { Transaction } from "../store/transferSlice";
|
import { Transaction } from "../store/transferSlice";
|
||||||
import { CHAINS_BY_ID, SOLANA_HOST } from "../utils/consts";
|
import { CHAINS_BY_ID, SOLANA_HOST } from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
root: {
|
root: {
|
||||||
|
@ -34,7 +35,7 @@ export default function TransactionProgress({
|
||||||
const [currentBlock, setCurrentBlock] = useState(0);
|
const [currentBlock, setCurrentBlock] = useState(0);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isSendComplete || !tx) return;
|
if (isSendComplete || !tx) return;
|
||||||
if (chainId === CHAIN_ID_ETH && provider) {
|
if (isEVMChain(chainId) && provider) {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
(async () => {
|
(async () => {
|
||||||
while (!cancelled) {
|
while (!cancelled) {
|
||||||
|
@ -73,7 +74,7 @@ export default function TransactionProgress({
|
||||||
chainId === CHAIN_ID_SOLANA ? 32 : chainId === CHAIN_ID_ETH ? 15 : 1;
|
chainId === CHAIN_ID_SOLANA ? 32 : chainId === CHAIN_ID_ETH ? 15 : 1;
|
||||||
if (
|
if (
|
||||||
!isSendComplete &&
|
!isSendComplete &&
|
||||||
(chainId === CHAIN_ID_SOLANA || chainId === CHAIN_ID_ETH) &&
|
(chainId === CHAIN_ID_SOLANA || isEVMChain(chainId)) &&
|
||||||
blockDiff !== undefined
|
blockDiff !== undefined
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { CHAIN_ID_ETH } from "@certusone/wormhole-sdk";
|
|
||||||
import { Button, makeStyles } from "@material-ui/core";
|
import { Button, makeStyles } from "@material-ui/core";
|
||||||
import detectEthereumProvider from "@metamask/detect-provider";
|
import detectEthereumProvider from "@metamask/detect-provider";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
|
@ -8,9 +7,11 @@ import {
|
||||||
selectTransferTargetAsset,
|
selectTransferTargetAsset,
|
||||||
selectTransferTargetChain,
|
selectTransferTargetChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
|
import { getEvmChainId } from "../../utils/consts";
|
||||||
import {
|
import {
|
||||||
ethTokenToParsedTokenAccount,
|
ethTokenToParsedTokenAccount,
|
||||||
getEthereumToken,
|
getEthereumToken,
|
||||||
|
isEVMChain,
|
||||||
} from "../../utils/ethereum";
|
} from "../../utils/ethereum";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
@ -24,9 +25,14 @@ export default function AddToMetamask() {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const targetChain = useSelector(selectTransferTargetChain);
|
const targetChain = useSelector(selectTransferTargetChain);
|
||||||
const targetAsset = useSelector(selectTransferTargetAsset);
|
const targetAsset = useSelector(selectTransferTargetAsset);
|
||||||
const { provider, signerAddress } = useEthereumProvider();
|
const {
|
||||||
|
provider,
|
||||||
|
signerAddress,
|
||||||
|
chainId: evmChainId,
|
||||||
|
} = useEthereumProvider();
|
||||||
|
const hasCorrectEvmNetwork = evmChainId === getEvmChainId(targetChain);
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
if (provider && targetAsset && signerAddress) {
|
if (provider && targetAsset && signerAddress && hasCorrectEvmNetwork) {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const token = await getEthereumToken(targetAsset, provider);
|
const token = await getEthereumToken(targetAsset, provider);
|
||||||
|
@ -52,11 +58,12 @@ export default function AddToMetamask() {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
}, [provider, targetAsset, signerAddress]);
|
}, [provider, targetAsset, signerAddress, hasCorrectEvmNetwork]);
|
||||||
return provider &&
|
return provider &&
|
||||||
signerAddress &&
|
signerAddress &&
|
||||||
targetAsset &&
|
targetAsset &&
|
||||||
targetChain === CHAIN_ID_ETH ? (
|
isEVMChain(targetChain) &&
|
||||||
|
hasCorrectEvmNetwork ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
size="small"
|
size="small"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_BSC,
|
ChainId,
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
getEmitterAddressEth,
|
getEmitterAddressEth,
|
||||||
|
@ -50,14 +49,15 @@ import {
|
||||||
} from "../../store/transferSlice";
|
} from "../../store/transferSlice";
|
||||||
import {
|
import {
|
||||||
CHAINS,
|
CHAINS,
|
||||||
ETH_BRIDGE_ADDRESS,
|
getBridgeAddressForChain,
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
TERRA_HOST,
|
TERRA_HOST,
|
||||||
TERRA_TOKEN_BRIDGE_ADDRESS,
|
TERRA_TOKEN_BRIDGE_ADDRESS,
|
||||||
WORMHOLE_RPC_HOSTS,
|
WORMHOLE_RPC_HOSTS,
|
||||||
} from "../../utils/consts";
|
} from "../../utils/consts";
|
||||||
|
import { isEVMChain } from "../../utils/ethereum";
|
||||||
import { getSignedVAAWithRetry } from "../../utils/getSignedVAAWithRetry";
|
import { getSignedVAAWithRetry } from "../../utils/getSignedVAAWithRetry";
|
||||||
import parseError from "../../utils/parseError";
|
import parseError from "../../utils/parseError";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
@ -70,17 +70,23 @@ const useStyles = makeStyles((theme) => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
async function eth(
|
async function evm(
|
||||||
provider: ethers.providers.Web3Provider,
|
provider: ethers.providers.Web3Provider,
|
||||||
tx: string,
|
tx: string,
|
||||||
enqueueSnackbar: any
|
enqueueSnackbar: any,
|
||||||
|
chainId: ChainId
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const receipt = await provider.getTransactionReceipt(tx);
|
const receipt = await provider.getTransactionReceipt(tx);
|
||||||
const sequence = parseSequenceFromLogEth(receipt, ETH_BRIDGE_ADDRESS);
|
const sequence = parseSequenceFromLogEth(
|
||||||
const emitterAddress = getEmitterAddressEth(ETH_TOKEN_BRIDGE_ADDRESS);
|
receipt,
|
||||||
|
getBridgeAddressForChain(chainId)
|
||||||
|
);
|
||||||
|
const emitterAddress = getEmitterAddressEth(
|
||||||
|
getTokenBridgeAddressForChain(chainId)
|
||||||
|
);
|
||||||
const { vaaBytes } = await getSignedVAAWithRetry(
|
const { vaaBytes } = await getSignedVAAWithRetry(
|
||||||
CHAIN_ID_ETH,
|
chainId,
|
||||||
emitterAddress,
|
emitterAddress,
|
||||||
sequence.toString(),
|
sequence.toString(),
|
||||||
WORMHOLE_RPC_HOSTS.length
|
WORMHOLE_RPC_HOSTS.length
|
||||||
|
@ -172,14 +178,15 @@ function RecoveryDialogContent({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (recoverySourceTx) {
|
if (recoverySourceTx) {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
if (recoverySourceChain === CHAIN_ID_ETH && provider) {
|
if (isEVMChain(recoverySourceChain) && provider) {
|
||||||
setRecoverySourceTxError("");
|
setRecoverySourceTxError("");
|
||||||
setRecoverySourceTxIsLoading(true);
|
setRecoverySourceTxIsLoading(true);
|
||||||
(async () => {
|
(async () => {
|
||||||
const { vaa, error } = await eth(
|
const { vaa, error } = await evm(
|
||||||
provider,
|
provider,
|
||||||
recoverySourceTx,
|
recoverySourceTx,
|
||||||
enqueueSnackbar
|
enqueueSnackbar,
|
||||||
|
recoverySourceChain
|
||||||
);
|
);
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setRecoverySourceTxIsLoading(false);
|
setRecoverySourceTxIsLoading(false);
|
||||||
|
@ -315,8 +322,7 @@ function RecoveryDialogContent({
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
{recoverySourceChain === CHAIN_ID_ETH ||
|
{isEVMChain(recoverySourceChain) ? (
|
||||||
recoverySourceChain === CHAIN_ID_BSC ? (
|
|
||||||
<KeyAndBalance chainId={recoverySourceChain} />
|
<KeyAndBalance chainId={recoverySourceChain} />
|
||||||
) : null}
|
) : null}
|
||||||
<TextField
|
<TextField
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
CHAIN_ID_BSC,
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
WSOL_ADDRESS,
|
WSOL_ADDRESS,
|
||||||
|
@ -12,7 +13,7 @@ import {
|
||||||
selectTransferTargetAsset,
|
selectTransferTargetAsset,
|
||||||
selectTransferTargetChain,
|
selectTransferTargetChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { WETH_ADDRESS } from "../../utils/consts";
|
import { WBNB_ADDRESS, WETH_ADDRESS } from "../../utils/consts";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
import StepDescription from "../StepDescription";
|
import StepDescription from "../StepDescription";
|
||||||
|
@ -29,11 +30,15 @@ function Redeem() {
|
||||||
targetChain === CHAIN_ID_ETH &&
|
targetChain === CHAIN_ID_ETH &&
|
||||||
targetAsset &&
|
targetAsset &&
|
||||||
targetAsset.toLowerCase() === WETH_ADDRESS.toLowerCase();
|
targetAsset.toLowerCase() === WETH_ADDRESS.toLowerCase();
|
||||||
|
const isBscNative =
|
||||||
|
targetChain === CHAIN_ID_BSC &&
|
||||||
|
targetAsset &&
|
||||||
|
targetAsset.toLowerCase() === WBNB_ADDRESS.toLowerCase();
|
||||||
const isSolNative =
|
const isSolNative =
|
||||||
targetChain === CHAIN_ID_SOLANA &&
|
targetChain === CHAIN_ID_SOLANA &&
|
||||||
targetAsset &&
|
targetAsset &&
|
||||||
targetAsset === WSOL_ADDRESS;
|
targetAsset === WSOL_ADDRESS;
|
||||||
const isNativeEligible = isEthNative || isSolNative;
|
const isNativeEligible = isEthNative || isBscNative || isSolNative;
|
||||||
const [useNativeRedeem, setUseNativeRedeem] = useState(true);
|
const [useNativeRedeem, setUseNativeRedeem] = useState(true);
|
||||||
const toggleNativeRedeem = useCallback(() => {
|
const toggleNativeRedeem = useCallback(() => {
|
||||||
setUseNativeRedeem(!useNativeRedeem);
|
setUseNativeRedeem(!useNativeRedeem);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { CHAIN_ID_ETH } from "@certusone/wormhole-sdk";
|
|
||||||
import { Checkbox, FormControlLabel } from "@material-ui/core";
|
import { Checkbox, FormControlLabel } from "@material-ui/core";
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
|
@ -19,6 +18,7 @@ import {
|
||||||
selectTransferTransferTx,
|
selectTransferTransferTx,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { CHAINS_BY_ID } from "../../utils/consts";
|
import { CHAINS_BY_ID } from "../../utils/consts";
|
||||||
|
import { isEVMChain } from "../../utils/ethereum";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
import ShowTx from "../ShowTx";
|
import ShowTx from "../ShowTx";
|
||||||
|
@ -70,8 +70,7 @@ function Send() {
|
||||||
approveAmount,
|
approveAmount,
|
||||||
} = useAllowance(sourceChain, sourceAsset, sourceAmountParsed || undefined);
|
} = useAllowance(sourceChain, sourceAsset, sourceAmountParsed || undefined);
|
||||||
|
|
||||||
const approveButtonNeeded =
|
const approveButtonNeeded = isEVMChain(sourceChain) && !sufficientAllowance;
|
||||||
sourceChain === CHAIN_ID_ETH && !sufficientAllowance;
|
|
||||||
const notOne = shouldApproveUnlimited || sourceAmountParsed !== oneParsed;
|
const notOne = shouldApproveUnlimited || sourceAmountParsed !== oneParsed;
|
||||||
const isDisabled =
|
const isDisabled =
|
||||||
!isReady ||
|
!isReady ||
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
import {
|
import { CHAIN_ID_SOLANA, hexToNativeString } from "@certusone/wormhole-sdk";
|
||||||
CHAIN_ID_SOLANA,
|
|
||||||
CHAIN_ID_ETH,
|
|
||||||
hexToNativeString,
|
|
||||||
} from "@certusone/wormhole-sdk";
|
|
||||||
import { makeStyles, MenuItem, TextField, Typography } from "@material-ui/core";
|
import { makeStyles, MenuItem, TextField, Typography } from "@material-ui/core";
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
|
@ -12,6 +8,7 @@ import useMetadata from "../../hooks/useMetadata";
|
||||||
import useSyncTargetAddress from "../../hooks/useSyncTargetAddress";
|
import useSyncTargetAddress from "../../hooks/useSyncTargetAddress";
|
||||||
import { EthGasEstimateSummary } from "../../hooks/useTransactionFees";
|
import { EthGasEstimateSummary } from "../../hooks/useTransactionFees";
|
||||||
import {
|
import {
|
||||||
|
selectTransferAmount,
|
||||||
selectTransferIsTargetComplete,
|
selectTransferIsTargetComplete,
|
||||||
selectTransferShouldLockFields,
|
selectTransferShouldLockFields,
|
||||||
selectTransferSourceChain,
|
selectTransferSourceChain,
|
||||||
|
@ -21,10 +18,10 @@ import {
|
||||||
selectTransferTargetChain,
|
selectTransferTargetChain,
|
||||||
selectTransferTargetError,
|
selectTransferTargetError,
|
||||||
UNREGISTERED_ERROR_MESSAGE,
|
UNREGISTERED_ERROR_MESSAGE,
|
||||||
selectTransferAmount,
|
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { incrementStep, setTargetChain } from "../../store/transferSlice";
|
import { incrementStep, setTargetChain } from "../../store/transferSlice";
|
||||||
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
||||||
|
import { isEVMChain } from "../../utils/ethereum";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
import LowBalanceWarning from "../LowBalanceWarning";
|
import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
|
@ -152,8 +149,8 @@ function Target() {
|
||||||
You will have to pay transaction fees on{" "}
|
You will have to pay transaction fees on{" "}
|
||||||
{CHAINS_BY_ID[targetChain].name} to redeem your tokens.
|
{CHAINS_BY_ID[targetChain].name} to redeem your tokens.
|
||||||
</Typography>
|
</Typography>
|
||||||
{targetChain === CHAIN_ID_ETH && (
|
{isEVMChain(targetChain) && (
|
||||||
<EthGasEstimateSummary methodType="transfer" />
|
<EthGasEstimateSummary methodType="transfer" chainId={targetChain} />
|
||||||
)}
|
)}
|
||||||
</Alert>
|
</Alert>
|
||||||
<LowBalanceWarning chainId={targetChain} />
|
<LowBalanceWarning chainId={targetChain} />
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
import {
|
import { approveEth, ChainId, getAllowanceEth } from "@certusone/wormhole-sdk";
|
||||||
approveEth,
|
|
||||||
ChainId,
|
|
||||||
CHAIN_ID_ETH,
|
|
||||||
getAllowanceEth,
|
|
||||||
} from "@certusone/wormhole-sdk";
|
|
||||||
import { BigNumber } from "ethers";
|
import { BigNumber } from "ethers";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
import { selectTransferIsApproving } from "../store/selectors";
|
import { selectTransferIsApproving } from "../store/selectors";
|
||||||
import { setIsApproving } from "../store/transferSlice";
|
import { setIsApproving } from "../store/transferSlice";
|
||||||
import { ETH_TOKEN_BRIDGE_ADDRESS } from "../utils/consts";
|
import { getTokenBridgeAddressForChain } from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
|
|
||||||
export default function useAllowance(
|
export default function useAllowance(
|
||||||
chainId: ChainId,
|
chainId: ChainId,
|
||||||
|
@ -23,19 +19,18 @@ export default function useAllowance(
|
||||||
const isApproveProcessing = useSelector(selectTransferIsApproving);
|
const isApproveProcessing = useSelector(selectTransferIsApproving);
|
||||||
const { signer } = useEthereumProvider();
|
const { signer } = useEthereumProvider();
|
||||||
const sufficientAllowance =
|
const sufficientAllowance =
|
||||||
chainId !== CHAIN_ID_ETH ||
|
!isEVMChain(chainId) ||
|
||||||
(allowance && transferAmount && allowance >= transferAmount);
|
(allowance && transferAmount && allowance >= transferAmount);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
if (
|
if (isEVMChain(chainId) && tokenAddress && signer && !isApproveProcessing) {
|
||||||
chainId === CHAIN_ID_ETH &&
|
|
||||||
tokenAddress &&
|
|
||||||
signer &&
|
|
||||||
!isApproveProcessing
|
|
||||||
) {
|
|
||||||
setIsAllowanceFetching(true);
|
setIsAllowanceFetching(true);
|
||||||
getAllowanceEth(ETH_TOKEN_BRIDGE_ADDRESS, tokenAddress, signer).then(
|
getAllowanceEth(
|
||||||
|
getTokenBridgeAddressForChain(chainId),
|
||||||
|
tokenAddress,
|
||||||
|
signer
|
||||||
|
).then(
|
||||||
(result) => {
|
(result) => {
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setIsAllowanceFetching(false);
|
setIsAllowanceFetching(false);
|
||||||
|
@ -57,14 +52,14 @@ export default function useAllowance(
|
||||||
}, [chainId, tokenAddress, signer, isApproveProcessing]);
|
}, [chainId, tokenAddress, signer, isApproveProcessing]);
|
||||||
|
|
||||||
const approveAmount: (amount: BigInt) => Promise<any> = useMemo(() => {
|
const approveAmount: (amount: BigInt) => Promise<any> = useMemo(() => {
|
||||||
return chainId !== CHAIN_ID_ETH || !tokenAddress || !signer
|
return !isEVMChain(chainId) || !tokenAddress || !signer
|
||||||
? (amount: BigInt) => {
|
? (amount: BigInt) => {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
: (amount: BigInt) => {
|
: (amount: BigInt) => {
|
||||||
dispatch(setIsApproving(true));
|
dispatch(setIsApproving(true));
|
||||||
return approveEth(
|
return approveEth(
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain(chainId),
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
signer,
|
signer,
|
||||||
BigNumber.from(amount)
|
BigNumber.from(amount)
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
getOriginalAssetEth,
|
getOriginalAssetEth,
|
||||||
getOriginalAssetSol,
|
getOriginalAssetSol,
|
||||||
getOriginalAssetTerra,
|
getOriginalAssetTerra,
|
||||||
WormholeWrappedInfo,
|
|
||||||
uint8ArrayToHex,
|
uint8ArrayToHex,
|
||||||
|
WormholeWrappedInfo,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
getOriginalAssetEth as getOriginalAssetEthNFT,
|
getOriginalAssetEth as getOriginalAssetEthNFT,
|
||||||
|
@ -18,6 +17,7 @@ import { LCDClient } from "@terra-money/terra.js";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
|
import { setSourceWormholeWrappedInfo as setNFTSourceWormholeWrappedInfo } from "../store/nftSlice";
|
||||||
import {
|
import {
|
||||||
selectNFTSourceAsset,
|
selectNFTSourceAsset,
|
||||||
selectNFTSourceChain,
|
selectNFTSourceChain,
|
||||||
|
@ -25,16 +25,16 @@ import {
|
||||||
selectTransferSourceAsset,
|
selectTransferSourceAsset,
|
||||||
selectTransferSourceChain,
|
selectTransferSourceChain,
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import { setSourceWormholeWrappedInfo as setNFTSourceWormholeWrappedInfo } from "../store/nftSlice";
|
|
||||||
import { setSourceWormholeWrappedInfo as setTransferSourceWormholeWrappedInfo } from "../store/transferSlice";
|
import { setSourceWormholeWrappedInfo as setTransferSourceWormholeWrappedInfo } from "../store/transferSlice";
|
||||||
import {
|
import {
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
getNFTBridgeAddressForChain,
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_NFT_BRIDGE_ADDRESS,
|
SOL_NFT_BRIDGE_ADDRESS,
|
||||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
TERRA_HOST,
|
TERRA_HOST,
|
||||||
} from "../utils/consts";
|
} from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
|
|
||||||
export interface StateSafeWormholeWrappedInfo {
|
export interface StateSafeWormholeWrappedInfo {
|
||||||
isWrapped: boolean;
|
isWrapped: boolean;
|
||||||
|
@ -74,19 +74,21 @@ function useCheckIfWormholeWrapped(nft?: boolean) {
|
||||||
dispatch(setSourceWormholeWrappedInfo(undefined));
|
dispatch(setSourceWormholeWrappedInfo(undefined));
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
(async () => {
|
(async () => {
|
||||||
if (sourceChain === CHAIN_ID_ETH && provider && sourceAsset) {
|
if (isEVMChain(sourceChain) && provider && sourceAsset) {
|
||||||
const wrappedInfo = makeStateSafe(
|
const wrappedInfo = makeStateSafe(
|
||||||
await (nft
|
await (nft
|
||||||
? getOriginalAssetEthNFT(
|
? getOriginalAssetEthNFT(
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
getNFTBridgeAddressForChain(sourceChain),
|
||||||
provider,
|
provider,
|
||||||
sourceAsset,
|
sourceAsset,
|
||||||
tokenId
|
tokenId,
|
||||||
|
sourceChain
|
||||||
)
|
)
|
||||||
: getOriginalAssetEth(
|
: getOriginalAssetEth(
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain(sourceChain),
|
||||||
provider,
|
provider,
|
||||||
sourceAsset
|
sourceAsset,
|
||||||
|
sourceChain
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { CHAIN_ID_ETH } from "@certusone/wormhole-sdk";
|
import { ChainId } from "@certusone/wormhole-sdk";
|
||||||
import { ethers } from "@certusone/wormhole-sdk/node_modules/ethers";
|
import { ethers } from "@certusone/wormhole-sdk/node_modules/ethers";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
|
@ -6,9 +6,10 @@ import {
|
||||||
useEthereumProvider,
|
useEthereumProvider,
|
||||||
} from "../contexts/EthereumProviderContext";
|
} from "../contexts/EthereumProviderContext";
|
||||||
import { DataWrapper } from "../store/helpers";
|
import { DataWrapper } from "../store/helpers";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
import useIsWalletReady from "./useIsWalletReady";
|
import useIsWalletReady from "./useIsWalletReady";
|
||||||
|
|
||||||
export type EthMetadata = {
|
export type EvmMetadata = {
|
||||||
symbol?: string;
|
symbol?: string;
|
||||||
logo?: string;
|
logo?: string;
|
||||||
tokenName?: string;
|
tokenName?: string;
|
||||||
|
@ -28,7 +29,7 @@ const handleError = () => {
|
||||||
const fetchSingleMetadata = async (
|
const fetchSingleMetadata = async (
|
||||||
address: string,
|
address: string,
|
||||||
provider: Provider
|
provider: Provider
|
||||||
): Promise<EthMetadata> => {
|
): Promise<EvmMetadata> => {
|
||||||
const contract = new ethers.Contract(address, ERC20_BASIC_ABI, provider);
|
const contract = new ethers.Contract(address, ERC20_BASIC_ABI, provider);
|
||||||
const [name, symbol, decimals] = await Promise.all([
|
const [name, symbol, decimals] = await Promise.all([
|
||||||
contract.name().catch(handleError),
|
contract.name().catch(handleError),
|
||||||
|
@ -39,12 +40,12 @@ const fetchSingleMetadata = async (
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchEthMetadata = async (addresses: string[], provider: Provider) => {
|
const fetchEthMetadata = async (addresses: string[], provider: Provider) => {
|
||||||
const promises: Promise<EthMetadata>[] = [];
|
const promises: Promise<EvmMetadata>[] = [];
|
||||||
addresses.forEach((address) => {
|
addresses.forEach((address) => {
|
||||||
promises.push(fetchSingleMetadata(address, provider));
|
promises.push(fetchSingleMetadata(address, provider));
|
||||||
});
|
});
|
||||||
const resultsArray = await Promise.all(promises);
|
const resultsArray = await Promise.all(promises);
|
||||||
const output = new Map<string, EthMetadata>();
|
const output = new Map<string, EvmMetadata>();
|
||||||
addresses.forEach((address, index) => {
|
addresses.forEach((address, index) => {
|
||||||
output.set(address, resultsArray[index]);
|
output.set(address, resultsArray[index]);
|
||||||
});
|
});
|
||||||
|
@ -52,19 +53,20 @@ const fetchEthMetadata = async (addresses: string[], provider: Provider) => {
|
||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
function useEthMetadata(
|
function useEvmMetadata(
|
||||||
addresses: string[]
|
addresses: string[],
|
||||||
): DataWrapper<Map<string, EthMetadata>> {
|
chainId: ChainId
|
||||||
const { isReady } = useIsWalletReady(CHAIN_ID_ETH);
|
): DataWrapper<Map<string, EvmMetadata>> {
|
||||||
|
const { isReady } = useIsWalletReady(chainId);
|
||||||
const { provider } = useEthereumProvider();
|
const { provider } = useEthereumProvider();
|
||||||
|
|
||||||
const [isFetching, setIsFetching] = useState(false);
|
const [isFetching, setIsFetching] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [data, setData] = useState<Map<string, EthMetadata> | null>(null);
|
const [data, setData] = useState<Map<string, EvmMetadata> | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
if (addresses.length && provider && isReady) {
|
if (addresses.length && provider && isReady && isEVMChain(chainId)) {
|
||||||
setIsFetching(true);
|
setIsFetching(true);
|
||||||
setError("");
|
setError("");
|
||||||
setData(null);
|
setData(null);
|
||||||
|
@ -86,7 +88,7 @@ function useEthMetadata(
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [addresses, provider, isReady]);
|
}, [addresses, provider, isReady, chainId]);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
@ -99,4 +101,4 @@ function useEthMetadata(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useEthMetadata;
|
export default useEvmMetadata;
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
getForeignAssetEth,
|
getForeignAssetEth,
|
||||||
|
@ -33,14 +32,16 @@ import {
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import { setTargetAsset as setTransferTargetAsset } from "../store/transferSlice";
|
import { setTargetAsset as setTransferTargetAsset } from "../store/transferSlice";
|
||||||
import {
|
import {
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
getEvmChainId,
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getNFTBridgeAddressForChain,
|
||||||
|
getTokenBridgeAddressForChain,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_NFT_BRIDGE_ADDRESS,
|
SOL_NFT_BRIDGE_ADDRESS,
|
||||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
TERRA_HOST,
|
TERRA_HOST,
|
||||||
TERRA_TOKEN_BRIDGE_ADDRESS,
|
TERRA_TOKEN_BRIDGE_ADDRESS,
|
||||||
} from "../utils/consts";
|
} from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
|
|
||||||
function useFetchTargetAsset(nft?: boolean) {
|
function useFetchTargetAsset(nft?: boolean) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
@ -61,32 +62,35 @@ function useFetchTargetAsset(nft?: boolean) {
|
||||||
nft ? selectNFTTargetChain : selectTransferTargetChain
|
nft ? selectNFTTargetChain : selectTransferTargetChain
|
||||||
);
|
);
|
||||||
const setTargetAsset = nft ? setNFTTargetAsset : setTransferTargetAsset;
|
const setTargetAsset = nft ? setNFTTargetAsset : setTransferTargetAsset;
|
||||||
const { provider } = useEthereumProvider();
|
const { provider, chainId: evmChainId } = useEthereumProvider();
|
||||||
|
const correctEvmNetwork = getEvmChainId(targetChain);
|
||||||
|
const hasCorrectEvmNetwork = evmChainId === correctEvmNetwork;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isSourceAssetWormholeWrapped && originChain === targetChain) {
|
if (isSourceAssetWormholeWrapped && originChain === targetChain) {
|
||||||
dispatch(setTargetAsset(hexToNativeString(originAsset, originChain)));
|
dispatch(setTargetAsset(hexToNativeString(originAsset, originChain)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: loading state, error state
|
// TODO: loading state, error state
|
||||||
dispatch(setTargetAsset(undefined));
|
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
(async () => {
|
(async () => {
|
||||||
if (
|
if (
|
||||||
targetChain === CHAIN_ID_ETH &&
|
isEVMChain(targetChain) &&
|
||||||
provider &&
|
provider &&
|
||||||
|
hasCorrectEvmNetwork &&
|
||||||
originChain &&
|
originChain &&
|
||||||
originAsset
|
originAsset
|
||||||
) {
|
) {
|
||||||
|
dispatch(setTargetAsset(undefined));
|
||||||
try {
|
try {
|
||||||
const asset = await (nft
|
const asset = await (nft
|
||||||
? getForeignAssetEthNFT(
|
? getForeignAssetEthNFT(
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
getNFTBridgeAddressForChain(targetChain),
|
||||||
provider,
|
provider,
|
||||||
originChain,
|
originChain,
|
||||||
hexToUint8Array(originAsset)
|
hexToUint8Array(originAsset)
|
||||||
)
|
)
|
||||||
: getForeignAssetEth(
|
: getForeignAssetEth(
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain(targetChain),
|
||||||
provider,
|
provider,
|
||||||
originChain,
|
originChain,
|
||||||
hexToUint8Array(originAsset)
|
hexToUint8Array(originAsset)
|
||||||
|
@ -102,6 +106,7 @@ function useFetchTargetAsset(nft?: boolean) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (targetChain === CHAIN_ID_SOLANA && originChain && originAsset) {
|
if (targetChain === CHAIN_ID_SOLANA && originChain && originAsset) {
|
||||||
|
dispatch(setTargetAsset(undefined));
|
||||||
try {
|
try {
|
||||||
const connection = new Connection(SOLANA_HOST, "confirmed");
|
const connection = new Connection(SOLANA_HOST, "confirmed");
|
||||||
const asset = await (nft
|
const asset = await (nft
|
||||||
|
@ -128,6 +133,7 @@ function useFetchTargetAsset(nft?: boolean) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (targetChain === CHAIN_ID_TERRA && originChain && originAsset) {
|
if (targetChain === CHAIN_ID_TERRA && originChain && originAsset) {
|
||||||
|
dispatch(setTargetAsset(undefined));
|
||||||
try {
|
try {
|
||||||
const lcd = new LCDClient(TERRA_HOST);
|
const lcd = new LCDClient(TERRA_HOST);
|
||||||
const asset = await getForeignAssetTerra(
|
const asset = await getForeignAssetTerra(
|
||||||
|
@ -160,6 +166,7 @@ function useFetchTargetAsset(nft?: boolean) {
|
||||||
nft,
|
nft,
|
||||||
setTargetAsset,
|
setTargetAsset,
|
||||||
tokenId,
|
tokenId,
|
||||||
|
hasCorrectEvmNetwork,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
TokenImplementation__factory,
|
TokenImplementation__factory,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { Connection, PublicKey } from "@solana/web3.js";
|
import { Connection, PublicKey } from "@solana/web3.js";
|
||||||
import { useConnectedWallet } from "@terra-money/wallet-provider";
|
|
||||||
import { LCDClient } from "@terra-money/terra.js";
|
import { LCDClient } from "@terra-money/terra.js";
|
||||||
|
import { useConnectedWallet } from "@terra-money/wallet-provider";
|
||||||
import { formatUnits } from "ethers/lib/utils";
|
import { formatUnits } from "ethers/lib/utils";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
@ -22,9 +21,12 @@ import {
|
||||||
setSourceParsedTokenAccount,
|
setSourceParsedTokenAccount,
|
||||||
setTargetParsedTokenAccount,
|
setTargetParsedTokenAccount,
|
||||||
} from "../store/transferSlice";
|
} from "../store/transferSlice";
|
||||||
import { SOLANA_HOST, TERRA_HOST } from "../utils/consts";
|
import { getEvmChainId, SOLANA_HOST, TERRA_HOST } from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
import { createParsedTokenAccount } from "./useGetSourceParsedTokenAccounts";
|
import { createParsedTokenAccount } from "./useGetSourceParsedTokenAccounts";
|
||||||
|
|
||||||
|
// TODO: we only ever use this for target, could clean up and rename
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the balance of an asset for the connected wallet
|
* Fetches the balance of an asset for the connected wallet
|
||||||
* @param sourceOrTarget determines whether this will fetch balance for the source or target account. Not intended to be switched on the same hook!
|
* @param sourceOrTarget determines whether this will fetch balance for the source or target account. Not intended to be switched on the same hook!
|
||||||
|
@ -48,7 +50,12 @@ function useGetBalanceEffect(sourceOrTarget: "source" | "target") {
|
||||||
const solanaWallet = useSolanaWallet();
|
const solanaWallet = useSolanaWallet();
|
||||||
const solPK = solanaWallet?.publicKey;
|
const solPK = solanaWallet?.publicKey;
|
||||||
const terraWallet = useConnectedWallet();
|
const terraWallet = useConnectedWallet();
|
||||||
const { provider, signerAddress } = useEthereumProvider();
|
const {
|
||||||
|
provider,
|
||||||
|
signerAddress,
|
||||||
|
chainId: evmChainId,
|
||||||
|
} = useEthereumProvider();
|
||||||
|
const hasCorrectEvmNetwork = evmChainId === getEvmChainId(lookupChain);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// source is now handled by getsourceparsedtokenaccounts
|
// source is now handled by getsourceparsedtokenaccounts
|
||||||
if (sourceOrTarget === "source") return;
|
if (sourceOrTarget === "source") return;
|
||||||
|
@ -127,7 +134,12 @@ function useGetBalanceEffect(sourceOrTarget: "source" | "target") {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (lookupChain === CHAIN_ID_ETH && provider && signerAddress) {
|
if (
|
||||||
|
isEVMChain(lookupChain) &&
|
||||||
|
provider &&
|
||||||
|
signerAddress &&
|
||||||
|
hasCorrectEvmNetwork
|
||||||
|
) {
|
||||||
const token = TokenImplementation__factory.connect(lookupAsset, provider);
|
const token = TokenImplementation__factory.connect(lookupAsset, provider);
|
||||||
token
|
token
|
||||||
.decimals()
|
.decimals()
|
||||||
|
@ -170,6 +182,7 @@ function useGetBalanceEffect(sourceOrTarget: "source" | "target") {
|
||||||
solPK,
|
solPK,
|
||||||
sourceOrTarget,
|
sourceOrTarget,
|
||||||
terraWallet,
|
terraWallet,
|
||||||
|
hasCorrectEvmNetwork,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import {
|
import {
|
||||||
|
ChainId,
|
||||||
|
CHAIN_ID_BSC,
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
|
@ -53,9 +55,12 @@ import {
|
||||||
import {
|
import {
|
||||||
COVALENT_GET_TOKENS_URL,
|
COVALENT_GET_TOKENS_URL,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
|
WBNB_ADDRESS,
|
||||||
|
WBNB_DECIMALS,
|
||||||
WETH_ADDRESS,
|
WETH_ADDRESS,
|
||||||
WETH_DECIMALS,
|
WETH_DECIMALS,
|
||||||
} from "../utils/consts";
|
} from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
import {
|
import {
|
||||||
ExtractedMintInfo,
|
ExtractedMintInfo,
|
||||||
extractMintInfo,
|
extractMintInfo,
|
||||||
|
@ -205,6 +210,29 @@ const createNativeEthParsedTokenAccount = (
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createNativeBscParsedTokenAccount = (
|
||||||
|
provider: Provider,
|
||||||
|
signerAddress: string | undefined
|
||||||
|
) => {
|
||||||
|
return !(provider && signerAddress)
|
||||||
|
? Promise.reject()
|
||||||
|
: provider.getBalance(signerAddress).then((balanceInWei) => {
|
||||||
|
const balanceInEth = ethers.utils.formatEther(balanceInWei);
|
||||||
|
return createParsedTokenAccount(
|
||||||
|
signerAddress, //public key
|
||||||
|
WBNB_ADDRESS, //Mint key, On the other side this will be WBNB, so this is hopefully a white lie.
|
||||||
|
balanceInWei.toString(), //amount, in wei
|
||||||
|
WBNB_DECIMALS, //Luckily both BNB and WBNB have 18 decimals, so this should not be an issue.
|
||||||
|
parseFloat(balanceInEth), //This loses precision, but is a limitation of the current datamodel. This field is essentially deprecated
|
||||||
|
balanceInEth.toString(), //This is the actual display field, which has full precision.
|
||||||
|
"BNB", //A white lie for display purposes
|
||||||
|
"Binance Coin", //A white lie for display purposes
|
||||||
|
undefined, //TODO logo
|
||||||
|
true //isNativeAsset
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const createNFTParsedTokenAccountFromCovalent = (
|
const createNFTParsedTokenAccountFromCovalent = (
|
||||||
walletAddress: string,
|
walletAddress: string,
|
||||||
covalent: CovalentData,
|
covalent: CovalentData,
|
||||||
|
@ -266,9 +294,10 @@ export type CovalentNFTData = {
|
||||||
|
|
||||||
const getEthereumAccountsCovalent = async (
|
const getEthereumAccountsCovalent = async (
|
||||||
walletAddress: string,
|
walletAddress: string,
|
||||||
nft?: boolean
|
nft: boolean,
|
||||||
|
chainId: ChainId
|
||||||
): Promise<CovalentData[]> => {
|
): Promise<CovalentData[]> => {
|
||||||
const url = COVALENT_GET_TOKENS_URL(CHAIN_ID_ETH, walletAddress, nft);
|
const url = COVALENT_GET_TOKENS_URL(chainId, walletAddress, nft);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const output = [] as CovalentData[];
|
const output = [] as CovalentData[];
|
||||||
|
@ -387,8 +416,7 @@ function useGetAvailableTokens(nft: boolean = false) {
|
||||||
const selectedSourceWalletAddress = useSelector(
|
const selectedSourceWalletAddress = useSelector(
|
||||||
nft ? selectNFTSourceWalletAddress : selectSourceWalletAddress
|
nft ? selectNFTSourceWalletAddress : selectSourceWalletAddress
|
||||||
);
|
);
|
||||||
const currentSourceWalletAddress: string | undefined =
|
const currentSourceWalletAddress: string | undefined = isEVMChain(lookupChain)
|
||||||
lookupChain === CHAIN_ID_ETH
|
|
||||||
? signerAddress
|
? signerAddress
|
||||||
: lookupChain === CHAIN_ID_SOLANA
|
: lookupChain === CHAIN_ID_SOLANA
|
||||||
? solPK?.toString()
|
? solPK?.toString()
|
||||||
|
@ -539,15 +567,50 @@ function useGetAvailableTokens(nft: boolean = false) {
|
||||||
};
|
};
|
||||||
}, [lookupChain, provider, signerAddress, nft, ethNativeAccount]);
|
}, [lookupChain, provider, signerAddress, nft, ethNativeAccount]);
|
||||||
|
|
||||||
|
//Binance Smart Chain native asset load
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
if (
|
||||||
|
signerAddress &&
|
||||||
|
lookupChain === CHAIN_ID_BSC &&
|
||||||
|
!ethNativeAccount &&
|
||||||
|
!nft
|
||||||
|
) {
|
||||||
|
setEthNativeAccountLoading(true);
|
||||||
|
createNativeBscParsedTokenAccount(provider, signerAddress).then(
|
||||||
|
(result) => {
|
||||||
|
console.log("create native account returned with value", result);
|
||||||
|
if (!cancelled) {
|
||||||
|
setEthNativeAccount(result);
|
||||||
|
setEthNativeAccountLoading(false);
|
||||||
|
setEthNativeAccountError("");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
if (!cancelled) {
|
||||||
|
setEthNativeAccount(undefined);
|
||||||
|
setEthNativeAccountLoading(false);
|
||||||
|
setEthNativeAccountError("Unable to retrieve your BSC balance.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [lookupChain, provider, signerAddress, nft, ethNativeAccount]);
|
||||||
|
|
||||||
//Ethereum covalent accounts load
|
//Ethereum covalent accounts load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//const testWallet = "0xf60c2ea62edbfe808163751dd0d8693dcb30019c";
|
//const testWallet = "0xf60c2ea62edbfe808163751dd0d8693dcb30019c";
|
||||||
// const nftTestWallet1 = "0x3f304c6721f35ff9af00fd32650c8e0a982180ab";
|
// const nftTestWallet1 = "0x3f304c6721f35ff9af00fd32650c8e0a982180ab";
|
||||||
// const nftTestWallet2 = "0x98ed231428088eb440e8edb5cc8d66dcf913b86e";
|
// const nftTestWallet2 = "0x98ed231428088eb440e8edb5cc8d66dcf913b86e";
|
||||||
// const nftTestWallet3 = "0xb1fadf677a7e9b90e9d4f31c8ffb3dc18c138c6f";
|
// const nftTestWallet3 = "0xb1fadf677a7e9b90e9d4f31c8ffb3dc18c138c6f";
|
||||||
|
// const nftBscTestWallet1 = "0x5f464a652bd1991df0be37979b93b3306d64a909";
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
const walletAddress = signerAddress;
|
const walletAddress = signerAddress;
|
||||||
if (walletAddress && lookupChain === CHAIN_ID_ETH && !covalent) {
|
if (walletAddress && isEVMChain(lookupChain) && !covalent) {
|
||||||
//TODO less cancel
|
//TODO less cancel
|
||||||
!cancelled && setCovalentLoading(true);
|
!cancelled && setCovalentLoading(true);
|
||||||
!cancelled &&
|
!cancelled &&
|
||||||
|
@ -556,7 +619,7 @@ function useGetAvailableTokens(nft: boolean = false) {
|
||||||
? fetchSourceParsedTokenAccountsNFT()
|
? fetchSourceParsedTokenAccountsNFT()
|
||||||
: fetchSourceParsedTokenAccounts()
|
: fetchSourceParsedTokenAccounts()
|
||||||
);
|
);
|
||||||
getEthereumAccountsCovalent(walletAddress, nft).then(
|
getEthereumAccountsCovalent(walletAddress, nft, lookupChain).then(
|
||||||
(accounts) => {
|
(accounts) => {
|
||||||
!cancelled && setCovalentLoading(false);
|
!cancelled && setCovalentLoading(false);
|
||||||
!cancelled && setCovalentError(undefined);
|
!cancelled && setCovalentError(undefined);
|
||||||
|
@ -639,7 +702,7 @@ function useGetAvailableTokens(nft: boolean = false) {
|
||||||
},
|
},
|
||||||
resetAccounts: resetSourceAccounts,
|
resetAccounts: resetSourceAccounts,
|
||||||
}
|
}
|
||||||
: lookupChain === CHAIN_ID_ETH
|
: isEVMChain(lookupChain)
|
||||||
? {
|
? {
|
||||||
tokenAccounts: ethAccounts,
|
tokenAccounts: ethAccounts,
|
||||||
covalent: {
|
covalent: {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {
|
||||||
attestFromEth,
|
attestFromEth,
|
||||||
attestFromSolana,
|
attestFromSolana,
|
||||||
attestFromTerra,
|
attestFromTerra,
|
||||||
CHAIN_ID_ETH,
|
ChainId,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
getEmitterAddressEth,
|
getEmitterAddressEth,
|
||||||
|
@ -19,10 +19,10 @@ import {
|
||||||
ConnectedWallet,
|
ConnectedWallet,
|
||||||
useConnectedWallet,
|
useConnectedWallet,
|
||||||
} from "@terra-money/wallet-provider";
|
} from "@terra-money/wallet-provider";
|
||||||
|
import { Signer } from "ethers";
|
||||||
import { useSnackbar } from "notistack";
|
import { useSnackbar } from "notistack";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { Signer } from "ethers";
|
|
||||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||||
import {
|
import {
|
||||||
|
@ -38,28 +38,30 @@ import {
|
||||||
selectAttestSourceChain,
|
selectAttestSourceChain,
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import {
|
import {
|
||||||
ETH_BRIDGE_ADDRESS,
|
getBridgeAddressForChain,
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_BRIDGE_ADDRESS,
|
SOL_BRIDGE_ADDRESS,
|
||||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
TERRA_TOKEN_BRIDGE_ADDRESS,
|
TERRA_TOKEN_BRIDGE_ADDRESS,
|
||||||
} from "../utils/consts";
|
} from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry";
|
import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry";
|
||||||
import parseError from "../utils/parseError";
|
import parseError from "../utils/parseError";
|
||||||
import { signSendAndConfirm } from "../utils/solana";
|
import { signSendAndConfirm } from "../utils/solana";
|
||||||
import { waitForTerraExecution } from "../utils/terra";
|
import { waitForTerraExecution } from "../utils/terra";
|
||||||
|
|
||||||
async function eth(
|
async function evm(
|
||||||
dispatch: any,
|
dispatch: any,
|
||||||
enqueueSnackbar: any,
|
enqueueSnackbar: any,
|
||||||
signer: Signer,
|
signer: Signer,
|
||||||
sourceAsset: string
|
sourceAsset: string,
|
||||||
|
chainId: ChainId
|
||||||
) {
|
) {
|
||||||
dispatch(setIsSending(true));
|
dispatch(setIsSending(true));
|
||||||
try {
|
try {
|
||||||
const receipt = await attestFromEth(
|
const receipt = await attestFromEth(
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain(chainId),
|
||||||
signer,
|
signer,
|
||||||
sourceAsset
|
sourceAsset
|
||||||
);
|
);
|
||||||
|
@ -67,11 +69,16 @@ async function eth(
|
||||||
setAttestTx({ id: receipt.transactionHash, block: receipt.blockNumber })
|
setAttestTx({ id: receipt.transactionHash, block: receipt.blockNumber })
|
||||||
);
|
);
|
||||||
enqueueSnackbar("Transaction confirmed", { variant: "success" });
|
enqueueSnackbar("Transaction confirmed", { variant: "success" });
|
||||||
const sequence = parseSequenceFromLogEth(receipt, ETH_BRIDGE_ADDRESS);
|
const sequence = parseSequenceFromLogEth(
|
||||||
const emitterAddress = getEmitterAddressEth(ETH_TOKEN_BRIDGE_ADDRESS);
|
receipt,
|
||||||
|
getBridgeAddressForChain(chainId)
|
||||||
|
);
|
||||||
|
const emitterAddress = getEmitterAddressEth(
|
||||||
|
getTokenBridgeAddressForChain(chainId)
|
||||||
|
);
|
||||||
enqueueSnackbar("Fetching VAA", { variant: "info" });
|
enqueueSnackbar("Fetching VAA", { variant: "info" });
|
||||||
const { vaaBytes } = await getSignedVAAWithRetry(
|
const { vaaBytes } = await getSignedVAAWithRetry(
|
||||||
CHAIN_ID_ETH,
|
chainId,
|
||||||
emitterAddress,
|
emitterAddress,
|
||||||
sequence
|
sequence
|
||||||
);
|
);
|
||||||
|
@ -180,8 +187,8 @@ export function useHandleAttest() {
|
||||||
const terraWallet = useConnectedWallet();
|
const terraWallet = useConnectedWallet();
|
||||||
const disabled = !isTargetComplete || isSending || isSendComplete;
|
const disabled = !isTargetComplete || isSending || isSendComplete;
|
||||||
const handleAttestClick = useCallback(() => {
|
const handleAttestClick = useCallback(() => {
|
||||||
if (sourceChain === CHAIN_ID_ETH && !!signer) {
|
if (isEVMChain(sourceChain) && !!signer) {
|
||||||
eth(dispatch, enqueueSnackbar, signer, sourceAsset);
|
evm(dispatch, enqueueSnackbar, signer, sourceAsset, sourceChain);
|
||||||
} else if (sourceChain === CHAIN_ID_SOLANA && !!solanaWallet && !!solPK) {
|
} else if (sourceChain === CHAIN_ID_SOLANA && !!solanaWallet && !!solPK) {
|
||||||
solana(dispatch, enqueueSnackbar, solPK, sourceAsset, solanaWallet);
|
solana(dispatch, enqueueSnackbar, solPK, sourceAsset, solanaWallet);
|
||||||
} else if (sourceChain === CHAIN_ID_TERRA && !!terraWallet) {
|
} else if (sourceChain === CHAIN_ID_TERRA && !!terraWallet) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_ETH,
|
ChainId,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
createWrappedOnEth,
|
createWrappedOnEth,
|
||||||
|
@ -26,25 +26,27 @@ import {
|
||||||
selectAttestTargetChain,
|
selectAttestTargetChain,
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import {
|
import {
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_BRIDGE_ADDRESS,
|
SOL_BRIDGE_ADDRESS,
|
||||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
TERRA_TOKEN_BRIDGE_ADDRESS,
|
TERRA_TOKEN_BRIDGE_ADDRESS,
|
||||||
} from "../utils/consts";
|
} from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
import parseError from "../utils/parseError";
|
import parseError from "../utils/parseError";
|
||||||
import { signSendAndConfirm } from "../utils/solana";
|
import { signSendAndConfirm } from "../utils/solana";
|
||||||
|
|
||||||
async function eth(
|
async function evm(
|
||||||
dispatch: any,
|
dispatch: any,
|
||||||
enqueueSnackbar: any,
|
enqueueSnackbar: any,
|
||||||
signer: Signer,
|
signer: Signer,
|
||||||
signedVAA: Uint8Array
|
signedVAA: Uint8Array,
|
||||||
|
chainId: ChainId
|
||||||
) {
|
) {
|
||||||
dispatch(setIsCreating(true));
|
dispatch(setIsCreating(true));
|
||||||
try {
|
try {
|
||||||
const receipt = await createWrappedOnEth(
|
const receipt = await createWrappedOnEth(
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain(chainId),
|
||||||
signer,
|
signer,
|
||||||
signedVAA
|
signedVAA
|
||||||
);
|
);
|
||||||
|
@ -133,8 +135,8 @@ export function useHandleCreateWrapped() {
|
||||||
const { signer } = useEthereumProvider();
|
const { signer } = useEthereumProvider();
|
||||||
const terraWallet = useConnectedWallet();
|
const terraWallet = useConnectedWallet();
|
||||||
const handleCreateClick = useCallback(() => {
|
const handleCreateClick = useCallback(() => {
|
||||||
if (targetChain === CHAIN_ID_ETH && !!signer && !!signedVAA) {
|
if (isEVMChain(targetChain) && !!signer && !!signedVAA) {
|
||||||
eth(dispatch, enqueueSnackbar, signer, signedVAA);
|
evm(dispatch, enqueueSnackbar, signer, signedVAA, targetChain);
|
||||||
} else if (
|
} else if (
|
||||||
targetChain === CHAIN_ID_SOLANA &&
|
targetChain === CHAIN_ID_SOLANA &&
|
||||||
!!solanaWallet &&
|
!!solanaWallet &&
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_ETH,
|
ChainId,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
getClaimAddressSolana,
|
getClaimAddressSolana,
|
||||||
postVaaSolana,
|
|
||||||
parseNFTPayload,
|
|
||||||
hexToUint8Array,
|
hexToUint8Array,
|
||||||
|
parseNFTPayload,
|
||||||
|
postVaaSolana,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
createMetaOnSolana,
|
createMetaOnSolana,
|
||||||
|
@ -25,26 +25,28 @@ import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||||
import { setIsRedeeming, setRedeemTx } from "../store/nftSlice";
|
import { setIsRedeeming, setRedeemTx } from "../store/nftSlice";
|
||||||
import { selectNFTIsRedeeming, selectNFTTargetChain } from "../store/selectors";
|
import { selectNFTIsRedeeming, selectNFTTargetChain } from "../store/selectors";
|
||||||
import {
|
import {
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
getNFTBridgeAddressForChain,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_BRIDGE_ADDRESS,
|
SOL_BRIDGE_ADDRESS,
|
||||||
SOL_NFT_BRIDGE_ADDRESS,
|
SOL_NFT_BRIDGE_ADDRESS,
|
||||||
} from "../utils/consts";
|
} from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
import { getMetadataAddress } from "../utils/metaplex";
|
import { getMetadataAddress } from "../utils/metaplex";
|
||||||
import parseError from "../utils/parseError";
|
import parseError from "../utils/parseError";
|
||||||
import { signSendAndConfirm } from "../utils/solana";
|
import { signSendAndConfirm } from "../utils/solana";
|
||||||
import useNFTSignedVAA from "./useNFTSignedVAA";
|
import useNFTSignedVAA from "./useNFTSignedVAA";
|
||||||
|
|
||||||
async function eth(
|
async function evm(
|
||||||
dispatch: any,
|
dispatch: any,
|
||||||
enqueueSnackbar: any,
|
enqueueSnackbar: any,
|
||||||
signer: Signer,
|
signer: Signer,
|
||||||
signedVAA: Uint8Array
|
signedVAA: Uint8Array,
|
||||||
|
chainId: ChainId
|
||||||
) {
|
) {
|
||||||
dispatch(setIsRedeeming(true));
|
dispatch(setIsRedeeming(true));
|
||||||
try {
|
try {
|
||||||
const receipt = await redeemOnEth(
|
const receipt = await redeemOnEth(
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
getNFTBridgeAddressForChain(chainId),
|
||||||
signer,
|
signer,
|
||||||
signedVAA
|
signedVAA
|
||||||
);
|
);
|
||||||
|
@ -142,8 +144,8 @@ export function useHandleNFTRedeem() {
|
||||||
const signedVAA = useNFTSignedVAA();
|
const signedVAA = useNFTSignedVAA();
|
||||||
const isRedeeming = useSelector(selectNFTIsRedeeming);
|
const isRedeeming = useSelector(selectNFTIsRedeeming);
|
||||||
const handleRedeemClick = useCallback(() => {
|
const handleRedeemClick = useCallback(() => {
|
||||||
if (targetChain === CHAIN_ID_ETH && !!signer && signedVAA) {
|
if (isEVMChain(targetChain) && !!signer && signedVAA) {
|
||||||
eth(dispatch, enqueueSnackbar, signer, signedVAA);
|
evm(dispatch, enqueueSnackbar, signer, signedVAA, targetChain);
|
||||||
} else if (
|
} else if (
|
||||||
targetChain === CHAIN_ID_SOLANA &&
|
targetChain === CHAIN_ID_SOLANA &&
|
||||||
!!solanaWallet &&
|
!!solanaWallet &&
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
getEmitterAddressEth,
|
getEmitterAddressEth,
|
||||||
getEmitterAddressSolana,
|
getEmitterAddressSolana,
|
||||||
|
hexToUint8Array,
|
||||||
parseSequenceFromLogEth,
|
parseSequenceFromLogEth,
|
||||||
parseSequenceFromLogSolana,
|
parseSequenceFromLogSolana,
|
||||||
hexToUint8Array,
|
|
||||||
uint8ArrayToHex,
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
|
@ -40,30 +39,32 @@ import {
|
||||||
selectNFTTargetChain,
|
selectNFTTargetChain,
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import {
|
import {
|
||||||
ETH_BRIDGE_ADDRESS,
|
getBridgeAddressForChain,
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
getNFTBridgeAddressForChain,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_BRIDGE_ADDRESS,
|
SOL_BRIDGE_ADDRESS,
|
||||||
SOL_NFT_BRIDGE_ADDRESS,
|
SOL_NFT_BRIDGE_ADDRESS,
|
||||||
} from "../utils/consts";
|
} from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry";
|
import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry";
|
||||||
import parseError from "../utils/parseError";
|
import parseError from "../utils/parseError";
|
||||||
import { signSendAndConfirm } from "../utils/solana";
|
import { signSendAndConfirm } from "../utils/solana";
|
||||||
import useNFTTargetAddressHex from "./useNFTTargetAddress";
|
import useNFTTargetAddressHex from "./useNFTTargetAddress";
|
||||||
|
|
||||||
async function eth(
|
async function evm(
|
||||||
dispatch: any,
|
dispatch: any,
|
||||||
enqueueSnackbar: any,
|
enqueueSnackbar: any,
|
||||||
signer: Signer,
|
signer: Signer,
|
||||||
tokenAddress: string,
|
tokenAddress: string,
|
||||||
tokenId: string,
|
tokenId: string,
|
||||||
recipientChain: ChainId,
|
recipientChain: ChainId,
|
||||||
recipientAddress: Uint8Array
|
recipientAddress: Uint8Array,
|
||||||
|
chainId: ChainId
|
||||||
) {
|
) {
|
||||||
dispatch(setIsSending(true));
|
dispatch(setIsSending(true));
|
||||||
try {
|
try {
|
||||||
const receipt = await transferFromEth(
|
const receipt = await transferFromEth(
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
getNFTBridgeAddressForChain(chainId),
|
||||||
signer,
|
signer,
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
tokenId,
|
tokenId,
|
||||||
|
@ -74,11 +75,16 @@ async function eth(
|
||||||
setTransferTx({ id: receipt.transactionHash, block: receipt.blockNumber })
|
setTransferTx({ id: receipt.transactionHash, block: receipt.blockNumber })
|
||||||
);
|
);
|
||||||
enqueueSnackbar("Transaction confirmed", { variant: "success" });
|
enqueueSnackbar("Transaction confirmed", { variant: "success" });
|
||||||
const sequence = parseSequenceFromLogEth(receipt, ETH_BRIDGE_ADDRESS);
|
const sequence = parseSequenceFromLogEth(
|
||||||
const emitterAddress = getEmitterAddressEth(ETH_NFT_BRIDGE_ADDRESS);
|
receipt,
|
||||||
|
getBridgeAddressForChain(chainId)
|
||||||
|
);
|
||||||
|
const emitterAddress = getEmitterAddressEth(
|
||||||
|
getNFTBridgeAddressForChain(chainId)
|
||||||
|
);
|
||||||
enqueueSnackbar("Fetching VAA", { variant: "info" });
|
enqueueSnackbar("Fetching VAA", { variant: "info" });
|
||||||
const { vaaBytes } = await getSignedVAAWithRetry(
|
const { vaaBytes } = await getSignedVAAWithRetry(
|
||||||
CHAIN_ID_ETH,
|
chainId,
|
||||||
emitterAddress,
|
emitterAddress,
|
||||||
sequence.toString()
|
sequence.toString()
|
||||||
);
|
);
|
||||||
|
@ -178,20 +184,21 @@ export function useHandleNFTTransfer() {
|
||||||
const handleTransferClick = useCallback(() => {
|
const handleTransferClick = useCallback(() => {
|
||||||
// TODO: we should separate state for transaction vs fetching vaa
|
// TODO: we should separate state for transaction vs fetching vaa
|
||||||
if (
|
if (
|
||||||
sourceChain === CHAIN_ID_ETH &&
|
isEVMChain(sourceChain) &&
|
||||||
!!signer &&
|
!!signer &&
|
||||||
!!sourceAsset &&
|
!!sourceAsset &&
|
||||||
!!sourceTokenId &&
|
!!sourceTokenId &&
|
||||||
!!targetAddress
|
!!targetAddress
|
||||||
) {
|
) {
|
||||||
eth(
|
evm(
|
||||||
dispatch,
|
dispatch,
|
||||||
enqueueSnackbar,
|
enqueueSnackbar,
|
||||||
signer,
|
signer,
|
||||||
sourceAsset,
|
sourceAsset,
|
||||||
sourceTokenId,
|
sourceTokenId,
|
||||||
targetChain,
|
targetChain,
|
||||||
targetAddress
|
targetAddress,
|
||||||
|
sourceChain
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
sourceChain === CHAIN_ID_SOLANA &&
|
sourceChain === CHAIN_ID_SOLANA &&
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_ETH,
|
ChainId,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
postVaaSolana,
|
postVaaSolana,
|
||||||
|
@ -28,27 +28,37 @@ import {
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import { setIsRedeeming, setRedeemTx } from "../store/transferSlice";
|
import { setIsRedeeming, setRedeemTx } from "../store/transferSlice";
|
||||||
import {
|
import {
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_BRIDGE_ADDRESS,
|
SOL_BRIDGE_ADDRESS,
|
||||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
TERRA_TOKEN_BRIDGE_ADDRESS,
|
TERRA_TOKEN_BRIDGE_ADDRESS,
|
||||||
} from "../utils/consts";
|
} from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
import parseError from "../utils/parseError";
|
import parseError from "../utils/parseError";
|
||||||
import { signSendAndConfirm } from "../utils/solana";
|
import { signSendAndConfirm } from "../utils/solana";
|
||||||
|
|
||||||
async function eth(
|
async function evm(
|
||||||
dispatch: any,
|
dispatch: any,
|
||||||
enqueueSnackbar: any,
|
enqueueSnackbar: any,
|
||||||
signer: Signer,
|
signer: Signer,
|
||||||
signedVAA: Uint8Array,
|
signedVAA: Uint8Array,
|
||||||
isNative: boolean
|
isNative: boolean,
|
||||||
|
chainId: ChainId
|
||||||
) {
|
) {
|
||||||
dispatch(setIsRedeeming(true));
|
dispatch(setIsRedeeming(true));
|
||||||
try {
|
try {
|
||||||
const receipt = isNative
|
const receipt = isNative
|
||||||
? await redeemOnEthNative(ETH_TOKEN_BRIDGE_ADDRESS, signer, signedVAA)
|
? await redeemOnEthNative(
|
||||||
: await redeemOnEth(ETH_TOKEN_BRIDGE_ADDRESS, signer, signedVAA);
|
getTokenBridgeAddressForChain(chainId),
|
||||||
|
signer,
|
||||||
|
signedVAA
|
||||||
|
)
|
||||||
|
: await redeemOnEth(
|
||||||
|
getTokenBridgeAddressForChain(chainId),
|
||||||
|
signer,
|
||||||
|
signedVAA
|
||||||
|
);
|
||||||
dispatch(
|
dispatch(
|
||||||
setRedeemTx({ id: receipt.transactionHash, block: receipt.blockNumber })
|
setRedeemTx({ id: receipt.transactionHash, block: receipt.blockNumber })
|
||||||
);
|
);
|
||||||
|
@ -144,8 +154,8 @@ export function useHandleRedeem() {
|
||||||
const signedVAA = useTransferSignedVAA();
|
const signedVAA = useTransferSignedVAA();
|
||||||
const isRedeeming = useSelector(selectTransferIsRedeeming);
|
const isRedeeming = useSelector(selectTransferIsRedeeming);
|
||||||
const handleRedeemClick = useCallback(() => {
|
const handleRedeemClick = useCallback(() => {
|
||||||
if (targetChain === CHAIN_ID_ETH && !!signer && signedVAA) {
|
if (isEVMChain(targetChain) && !!signer && signedVAA) {
|
||||||
eth(dispatch, enqueueSnackbar, signer, signedVAA, false);
|
evm(dispatch, enqueueSnackbar, signer, signedVAA, false, targetChain);
|
||||||
} else if (
|
} else if (
|
||||||
targetChain === CHAIN_ID_SOLANA &&
|
targetChain === CHAIN_ID_SOLANA &&
|
||||||
!!solanaWallet &&
|
!!solanaWallet &&
|
||||||
|
@ -179,8 +189,8 @@ export function useHandleRedeem() {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleRedeemNativeClick = useCallback(() => {
|
const handleRedeemNativeClick = useCallback(() => {
|
||||||
if (targetChain === CHAIN_ID_ETH && !!signer && signedVAA) {
|
if (isEVMChain(targetChain) && !!signer && signedVAA) {
|
||||||
eth(dispatch, enqueueSnackbar, signer, signedVAA, true);
|
evm(dispatch, enqueueSnackbar, signer, signedVAA, true, targetChain);
|
||||||
} else if (
|
} else if (
|
||||||
targetChain === CHAIN_ID_SOLANA &&
|
targetChain === CHAIN_ID_SOLANA &&
|
||||||
!!solanaWallet &&
|
!!solanaWallet &&
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
getEmitterAddressEth,
|
getEmitterAddressEth,
|
||||||
getEmitterAddressSolana,
|
getEmitterAddressSolana,
|
||||||
getEmitterAddressTerra,
|
getEmitterAddressTerra,
|
||||||
|
hexToUint8Array,
|
||||||
parseSequenceFromLogEth,
|
parseSequenceFromLogEth,
|
||||||
parseSequenceFromLogSolana,
|
parseSequenceFromLogSolana,
|
||||||
parseSequenceFromLogTerra,
|
parseSequenceFromLogTerra,
|
||||||
transferFromEth,
|
transferFromEth,
|
||||||
transferFromEthNative,
|
transferFromEthNative,
|
||||||
transferFromSolana,
|
transferFromSolana,
|
||||||
transferNativeSol,
|
|
||||||
transferFromTerra,
|
transferFromTerra,
|
||||||
hexToUint8Array,
|
transferNativeSol,
|
||||||
uint8ArrayToHex,
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { WalletContextState } from "@solana/wallet-adapter-react";
|
import { WalletContextState } from "@solana/wallet-adapter-react";
|
||||||
|
@ -48,20 +47,21 @@ import {
|
||||||
setTransferTx,
|
setTransferTx,
|
||||||
} from "../store/transferSlice";
|
} from "../store/transferSlice";
|
||||||
import {
|
import {
|
||||||
ETH_BRIDGE_ADDRESS,
|
getBridgeAddressForChain,
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
SOL_BRIDGE_ADDRESS,
|
SOL_BRIDGE_ADDRESS,
|
||||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
TERRA_TOKEN_BRIDGE_ADDRESS,
|
TERRA_TOKEN_BRIDGE_ADDRESS,
|
||||||
} from "../utils/consts";
|
} from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry";
|
import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry";
|
||||||
import parseError from "../utils/parseError";
|
import parseError from "../utils/parseError";
|
||||||
import { signSendAndConfirm } from "../utils/solana";
|
import { signSendAndConfirm } from "../utils/solana";
|
||||||
import { waitForTerraExecution } from "../utils/terra";
|
import { waitForTerraExecution } from "../utils/terra";
|
||||||
import useTransferTargetAddressHex from "./useTransferTargetAddress";
|
import useTransferTargetAddressHex from "./useTransferTargetAddress";
|
||||||
|
|
||||||
async function eth(
|
async function evm(
|
||||||
dispatch: any,
|
dispatch: any,
|
||||||
enqueueSnackbar: any,
|
enqueueSnackbar: any,
|
||||||
signer: Signer,
|
signer: Signer,
|
||||||
|
@ -70,21 +70,22 @@ async function eth(
|
||||||
amount: string,
|
amount: string,
|
||||||
recipientChain: ChainId,
|
recipientChain: ChainId,
|
||||||
recipientAddress: Uint8Array,
|
recipientAddress: Uint8Array,
|
||||||
isNative: boolean
|
isNative: boolean,
|
||||||
|
chainId: ChainId
|
||||||
) {
|
) {
|
||||||
dispatch(setIsSending(true));
|
dispatch(setIsSending(true));
|
||||||
try {
|
try {
|
||||||
const amountParsed = parseUnits(amount, decimals);
|
const amountParsed = parseUnits(amount, decimals);
|
||||||
const receipt = isNative
|
const receipt = isNative
|
||||||
? await transferFromEthNative(
|
? await transferFromEthNative(
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain(chainId),
|
||||||
signer,
|
signer,
|
||||||
amountParsed,
|
amountParsed,
|
||||||
recipientChain,
|
recipientChain,
|
||||||
recipientAddress
|
recipientAddress
|
||||||
)
|
)
|
||||||
: await transferFromEth(
|
: await transferFromEth(
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
getTokenBridgeAddressForChain(chainId),
|
||||||
signer,
|
signer,
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
amountParsed,
|
amountParsed,
|
||||||
|
@ -95,11 +96,16 @@ async function eth(
|
||||||
setTransferTx({ id: receipt.transactionHash, block: receipt.blockNumber })
|
setTransferTx({ id: receipt.transactionHash, block: receipt.blockNumber })
|
||||||
);
|
);
|
||||||
enqueueSnackbar("Transaction confirmed", { variant: "success" });
|
enqueueSnackbar("Transaction confirmed", { variant: "success" });
|
||||||
const sequence = parseSequenceFromLogEth(receipt, ETH_BRIDGE_ADDRESS);
|
const sequence = parseSequenceFromLogEth(
|
||||||
const emitterAddress = getEmitterAddressEth(ETH_TOKEN_BRIDGE_ADDRESS);
|
receipt,
|
||||||
|
getBridgeAddressForChain(chainId)
|
||||||
|
);
|
||||||
|
const emitterAddress = getEmitterAddressEth(
|
||||||
|
getTokenBridgeAddressForChain(chainId)
|
||||||
|
);
|
||||||
enqueueSnackbar("Fetching VAA", { variant: "info" });
|
enqueueSnackbar("Fetching VAA", { variant: "info" });
|
||||||
const { vaaBytes } = await getSignedVAAWithRetry(
|
const { vaaBytes } = await getSignedVAAWithRetry(
|
||||||
CHAIN_ID_ETH,
|
chainId,
|
||||||
emitterAddress,
|
emitterAddress,
|
||||||
sequence.toString()
|
sequence.toString()
|
||||||
);
|
);
|
||||||
|
@ -262,13 +268,13 @@ export function useHandleTransfer() {
|
||||||
const handleTransferClick = useCallback(() => {
|
const handleTransferClick = useCallback(() => {
|
||||||
// TODO: we should separate state for transaction vs fetching vaa
|
// TODO: we should separate state for transaction vs fetching vaa
|
||||||
if (
|
if (
|
||||||
sourceChain === CHAIN_ID_ETH &&
|
isEVMChain(sourceChain) &&
|
||||||
!!signer &&
|
!!signer &&
|
||||||
!!sourceAsset &&
|
!!sourceAsset &&
|
||||||
decimals !== undefined &&
|
decimals !== undefined &&
|
||||||
!!targetAddress
|
!!targetAddress
|
||||||
) {
|
) {
|
||||||
eth(
|
evm(
|
||||||
dispatch,
|
dispatch,
|
||||||
enqueueSnackbar,
|
enqueueSnackbar,
|
||||||
signer,
|
signer,
|
||||||
|
@ -277,7 +283,8 @@ export function useHandleTransfer() {
|
||||||
amount,
|
amount,
|
||||||
targetChain,
|
targetChain,
|
||||||
targetAddress,
|
targetAddress,
|
||||||
isNative
|
isNative,
|
||||||
|
sourceChain
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
sourceChain === CHAIN_ID_SOLANA &&
|
sourceChain === CHAIN_ID_SOLANA &&
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
|
@ -9,7 +8,8 @@ import { useConnectedWallet } from "@terra-money/wallet-provider";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||||
import { CLUSTER, ETH_NETWORK_CHAIN_ID } from "../utils/consts";
|
import { CLUSTER, getEvmChainId } from "../utils/consts";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
|
|
||||||
const createWalletStatus = (
|
const createWalletStatus = (
|
||||||
isReady: boolean,
|
isReady: boolean,
|
||||||
|
@ -33,10 +33,11 @@ function useIsWalletReady(chainId: ChainId): {
|
||||||
const {
|
const {
|
||||||
provider,
|
provider,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
chainId: ethChainId,
|
chainId: evmChainId,
|
||||||
} = useEthereumProvider();
|
} = useEthereumProvider();
|
||||||
const hasEthInfo = !!provider && !!signerAddress;
|
const hasEthInfo = !!provider && !!signerAddress;
|
||||||
const hasCorrectEthNetwork = ethChainId === ETH_NETWORK_CHAIN_ID;
|
const correctEvmNetwork = getEvmChainId(chainId);
|
||||||
|
const hasCorrectEvmNetwork = evmChainId === correctEvmNetwork;
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
if (
|
if (
|
||||||
|
@ -50,20 +51,20 @@ function useIsWalletReady(chainId: ChainId): {
|
||||||
if (chainId === CHAIN_ID_SOLANA && solPK) {
|
if (chainId === CHAIN_ID_SOLANA && solPK) {
|
||||||
return createWalletStatus(true, undefined, solPK.toString());
|
return createWalletStatus(true, undefined, solPK.toString());
|
||||||
}
|
}
|
||||||
if (chainId === CHAIN_ID_ETH && hasEthInfo && signerAddress) {
|
if (isEVMChain(chainId) && hasEthInfo && signerAddress) {
|
||||||
if (hasCorrectEthNetwork) {
|
if (hasCorrectEvmNetwork) {
|
||||||
return createWalletStatus(true, undefined, signerAddress);
|
return createWalletStatus(true, undefined, signerAddress);
|
||||||
} else {
|
} else {
|
||||||
if (provider) {
|
if (provider && correctEvmNetwork) {
|
||||||
try {
|
try {
|
||||||
provider.send("wallet_switchEthereumChain", [
|
provider.send("wallet_switchEthereumChain", [
|
||||||
{ chainId: hexStripZeros(hexlify(ETH_NETWORK_CHAIN_ID)) },
|
{ chainId: hexStripZeros(hexlify(correctEvmNetwork)) },
|
||||||
]);
|
]);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
return createWalletStatus(
|
return createWalletStatus(
|
||||||
false,
|
false,
|
||||||
`Wallet is not connected to ${CLUSTER}. Expected Chain ID: ${ETH_NETWORK_CHAIN_ID}`,
|
`Wallet is not connected to ${CLUSTER}. Expected Chain ID: ${correctEvmNetwork}`,
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -75,7 +76,8 @@ function useIsWalletReady(chainId: ChainId): {
|
||||||
hasTerraWallet,
|
hasTerraWallet,
|
||||||
solPK,
|
solPK,
|
||||||
hasEthInfo,
|
hasEthInfo,
|
||||||
hasCorrectEthNetwork,
|
correctEvmNetwork,
|
||||||
|
hasCorrectEvmNetwork,
|
||||||
provider,
|
provider,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
terraWallet,
|
terraWallet,
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { TokenInfo } from "@solana/spl-token-registry";
|
import { TokenInfo } from "@solana/spl-token-registry";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { DataWrapper, getEmptyDataWrapper } from "../store/helpers";
|
import { DataWrapper, getEmptyDataWrapper } from "../store/helpers";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
import { Metadata } from "../utils/metaplex";
|
import { Metadata } from "../utils/metaplex";
|
||||||
import useEthMetadata, { EthMetadata } from "./useEthMetadata";
|
import useEvmMetadata, { EvmMetadata } from "./useEvmMetadata";
|
||||||
import useMetaplexData from "./useMetaplexData";
|
import useMetaplexData from "./useMetaplexData";
|
||||||
import useSolanaTokenMap from "./useSolanaTokenMap";
|
import useSolanaTokenMap from "./useSolanaTokenMap";
|
||||||
import useTerraTokenMap, { TerraTokenMap } from "./useTerraTokenMap";
|
import useTerraTokenMap, { TerraTokenMap } from "./useTerraTokenMap";
|
||||||
|
@ -80,7 +80,7 @@ const constructTerraMetadata = (
|
||||||
|
|
||||||
const constructEthMetadata = (
|
const constructEthMetadata = (
|
||||||
addresses: string[],
|
addresses: string[],
|
||||||
metadataMap: DataWrapper<Map<string, EthMetadata> | null>
|
metadataMap: DataWrapper<Map<string, EvmMetadata> | null>
|
||||||
) => {
|
) => {
|
||||||
const isFetching = metadataMap.isFetching;
|
const isFetching = metadataMap.isFetching;
|
||||||
const error = metadataMap.error;
|
const error = metadataMap.error;
|
||||||
|
@ -119,17 +119,17 @@ export default function useMetadata(
|
||||||
return chainId === CHAIN_ID_TERRA ? addresses : [];
|
return chainId === CHAIN_ID_TERRA ? addresses : [];
|
||||||
}, [chainId, addresses]);
|
}, [chainId, addresses]);
|
||||||
const ethereumAddresses = useMemo(() => {
|
const ethereumAddresses = useMemo(() => {
|
||||||
return chainId === CHAIN_ID_ETH ? addresses : [];
|
return isEVMChain(chainId) ? addresses : [];
|
||||||
}, [chainId, addresses]);
|
}, [chainId, addresses]);
|
||||||
|
|
||||||
const metaplexData = useMetaplexData(solanaAddresses);
|
const metaplexData = useMetaplexData(solanaAddresses);
|
||||||
const ethMetadata = useEthMetadata(ethereumAddresses);
|
const ethMetadata = useEvmMetadata(ethereumAddresses, chainId);
|
||||||
|
|
||||||
const output: DataWrapper<Map<string, GenericMetadata>> = useMemo(
|
const output: DataWrapper<Map<string, GenericMetadata>> = useMemo(
|
||||||
() =>
|
() =>
|
||||||
chainId === CHAIN_ID_SOLANA
|
chainId === CHAIN_ID_SOLANA
|
||||||
? constructSolanaMetadata(solanaAddresses, solanaTokenMap, metaplexData)
|
? constructSolanaMetadata(solanaAddresses, solanaTokenMap, metaplexData)
|
||||||
: chainId === CHAIN_ID_ETH
|
: isEVMChain(chainId)
|
||||||
? constructEthMetadata(ethereumAddresses, ethMetadata)
|
? constructEthMetadata(ethereumAddresses, ethMetadata)
|
||||||
: chainId === CHAIN_ID_TERRA
|
: chainId === CHAIN_ID_TERRA
|
||||||
? constructTerraMetadata(terraAddresses, terraTokenMap)
|
? constructTerraMetadata(terraAddresses, terraTokenMap)
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import {
|
import {
|
||||||
CHAIN_ID_ETH,
|
canonicalAddress,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
canonicalAddress,
|
|
||||||
uint8ArrayToHex,
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { arrayify, zeroPad } from "@ethersproject/bytes";
|
import { arrayify, zeroPad } from "@ethersproject/bytes";
|
||||||
|
@ -17,6 +16,7 @@ import { useEffect } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||||
|
import { setTargetAddressHex as setNFTTargetAddressHex } from "../store/nftSlice";
|
||||||
import {
|
import {
|
||||||
selectNFTTargetAsset,
|
selectNFTTargetAsset,
|
||||||
selectNFTTargetChain,
|
selectNFTTargetChain,
|
||||||
|
@ -24,8 +24,8 @@ import {
|
||||||
selectTransferTargetChain,
|
selectTransferTargetChain,
|
||||||
selectTransferTargetParsedTokenAccount,
|
selectTransferTargetParsedTokenAccount,
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import { setTargetAddressHex as setNFTTargetAddressHex } from "../store/nftSlice";
|
|
||||||
import { setTargetAddressHex as setTransferTargetAddressHex } from "../store/transferSlice";
|
import { setTargetAddressHex as setTransferTargetAddressHex } from "../store/transferSlice";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
|
|
||||||
function useSyncTargetAddress(shouldFire: boolean, nft?: boolean) {
|
function useSyncTargetAddress(shouldFire: boolean, nft?: boolean) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
@ -49,7 +49,7 @@ function useSyncTargetAddress(shouldFire: boolean, nft?: boolean) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (shouldFire) {
|
if (shouldFire) {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
if (targetChain === CHAIN_ID_ETH && signerAddress) {
|
if (isEVMChain(targetChain) && signerAddress) {
|
||||||
dispatch(
|
dispatch(
|
||||||
setTargetAddressHex(
|
setTargetAddressHex(
|
||||||
uint8ArrayToHex(zeroPad(arrayify(signerAddress), 32))
|
uint8ArrayToHex(zeroPad(arrayify(signerAddress), 32))
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { LocalGasStation } from "@material-ui/icons";
|
||||||
import { Connection, PublicKey } from "@solana/web3.js";
|
import { Connection, PublicKey } from "@solana/web3.js";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
import { SOLANA_HOST } from "../utils/consts";
|
import { getDefaultNativeCurrencySymbol, SOLANA_HOST } from "../utils/consts";
|
||||||
import { getMultipleAccountsRPC } from "../utils/solana";
|
import { getMultipleAccountsRPC } from "../utils/solana";
|
||||||
import useIsWalletReady from "./useIsWalletReady";
|
import useIsWalletReady from "./useIsWalletReady";
|
||||||
|
|
||||||
|
@ -142,9 +142,9 @@ export default function useTransactionFees(chainId: ChainId) {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useEthereumGasPrice(contract: MethodType) {
|
export function useEthereumGasPrice(contract: MethodType, chainId: ChainId) {
|
||||||
const { provider } = useEthereumProvider();
|
const { provider } = useEthereumProvider();
|
||||||
const { isReady } = useIsWalletReady(CHAIN_ID_ETH);
|
const { isReady } = useIsWalletReady(chainId);
|
||||||
const [estimateResults, setEstimateResults] = useState<GasEstimate | null>(
|
const [estimateResults, setEstimateResults] = useState<GasEstimate | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
@ -168,10 +168,12 @@ export function useEthereumGasPrice(contract: MethodType) {
|
||||||
|
|
||||||
export function EthGasEstimateSummary({
|
export function EthGasEstimateSummary({
|
||||||
methodType,
|
methodType,
|
||||||
|
chainId,
|
||||||
}: {
|
}: {
|
||||||
methodType: MethodType;
|
methodType: MethodType;
|
||||||
|
chainId: ChainId;
|
||||||
}) {
|
}) {
|
||||||
const estimate = useEthereumGasPrice(methodType);
|
const estimate = useEthereumGasPrice(methodType, chainId);
|
||||||
if (!estimate) {
|
if (!estimate) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -192,7 +194,8 @@ export function EthGasEstimateSummary({
|
||||||
</div>
|
</div>
|
||||||
<div> </div>
|
<div> </div>
|
||||||
<div>
|
<div>
|
||||||
Est. Fees: {estimate.lowEstimate} - {estimate.highEstimate} ETH
|
Est. Fees: {estimate.lowEstimate} - {estimate.highEstimate}{" "}
|
||||||
|
{getDefaultNativeCurrencySymbol(chainId)}
|
||||||
</div>
|
</div>
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M20.3025 0L9.67897 6.12683L13.5847 8.39024L20.3025 4.52683L27.0204 8.39024L30.9261 6.12683L20.3025 0Z" fill="#F0B90B"/>
|
||||||
|
<path d="M27.0204 11.5902L30.9261 13.8537V18.3805L24.2083 22.2439V29.9707L20.3025 32.2341L16.3968 29.9707V22.2439L9.67897 18.3805V13.8537L13.5847 11.5902L20.3025 15.4537L27.0204 11.5902Z" fill="#F0B90B"/>
|
||||||
|
<path d="M30.9261 21.5805V26.1073L27.0204 28.3707V23.8439L30.9261 21.5805Z" fill="#F0B90B"/>
|
||||||
|
<path d="M26.9814 31.5707L33.6992 27.7073V19.9805L37.605 17.7171V29.9707L26.9814 36.0976V31.5707Z" fill="#F0B90B"/>
|
||||||
|
<path d="M33.6992 12.2537L29.7935 9.99025L33.6992 7.72683L37.605 9.99025V14.5171L33.6992 16.7805V12.2537Z" fill="#F0B90B"/>
|
||||||
|
<path d="M16.3968 37.7366V33.2098L20.3025 35.4732L24.2083 33.2098V37.7366L20.3025 40L16.3968 37.7366Z" fill="#F0B90B"/>
|
||||||
|
<path d="M13.5847 28.3707L9.67897 26.1073V21.5805L13.5847 23.8439V28.3707Z" fill="#F0B90B"/>
|
||||||
|
<path d="M20.3025 12.2537L16.3968 9.99025L20.3025 7.72683L24.2083 9.99025L20.3025 12.2537Z" fill="#F0B90B"/>
|
||||||
|
<path d="M10.8116 9.99025L6.90586 12.2537V16.7805L3.00012 14.5171V9.99025L6.90586 7.72683L10.8116 9.99025Z" fill="#F0B90B"/>
|
||||||
|
<path d="M3.00012 17.7171L6.90586 19.9805V27.7073L13.6237 31.5707V36.0976L3.00012 29.9707V17.7171Z" fill="#F0B90B"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -1,7 +1,8 @@
|
||||||
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
import { CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { parseUnits } from "ethers/lib/utils";
|
import { parseUnits } from "ethers/lib/utils";
|
||||||
import { RootState } from ".";
|
import { RootState } from ".";
|
||||||
|
import { isEVMChain } from "../utils/ethereum";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Attest
|
* Attest
|
||||||
|
@ -259,7 +260,7 @@ export const selectTransferTargetError = (state: RootState) => {
|
||||||
return UNREGISTERED_ERROR_MESSAGE;
|
return UNREGISTERED_ERROR_MESSAGE;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
state.transfer.targetChain === CHAIN_ID_ETH &&
|
isEVMChain(state.transfer.targetChain) &&
|
||||||
state.transfer.targetAsset === ethers.constants.AddressZero
|
state.transfer.targetAsset === ethers.constants.AddressZero
|
||||||
) {
|
) {
|
||||||
return UNREGISTERED_ERROR_MESSAGE;
|
return UNREGISTERED_ERROR_MESSAGE;
|
||||||
|
|
|
@ -64,11 +64,33 @@ export const CHAINS =
|
||||||
name: "Terra",
|
name: "Terra",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
export const CHAINS_WITH_NFT_SUPPORT = CHAINS.filter(
|
||||||
|
({ id }) =>
|
||||||
|
id === CHAIN_ID_ETH || id === CHAIN_ID_BSC || id === CHAIN_ID_SOLANA
|
||||||
|
);
|
||||||
export type ChainsById = { [key in ChainId]: ChainInfo };
|
export type ChainsById = { [key in ChainId]: ChainInfo };
|
||||||
export const CHAINS_BY_ID: ChainsById = CHAINS.reduce((obj, chain) => {
|
export const CHAINS_BY_ID: ChainsById = CHAINS.reduce((obj, chain) => {
|
||||||
obj[chain.id] = chain;
|
obj[chain.id] = chain;
|
||||||
return obj;
|
return obj;
|
||||||
}, {} as ChainsById);
|
}, {} as ChainsById);
|
||||||
|
export const getDefaultNativeCurrencySymbol = (chainId: ChainId) =>
|
||||||
|
chainId === CHAIN_ID_SOLANA
|
||||||
|
? "SOL"
|
||||||
|
: chainId === CHAIN_ID_ETH
|
||||||
|
? "ETH"
|
||||||
|
: chainId === CHAIN_ID_BSC
|
||||||
|
? "BNB"
|
||||||
|
: chainId === CHAIN_ID_TERRA
|
||||||
|
? "LUNA"
|
||||||
|
: "";
|
||||||
|
export const getExplorerName = (chainId: ChainId) =>
|
||||||
|
chainId === CHAIN_ID_ETH
|
||||||
|
? "Etherscan"
|
||||||
|
: chainId === CHAIN_ID_BSC
|
||||||
|
? "BscScan"
|
||||||
|
: chainId === CHAIN_ID_TERRA
|
||||||
|
? "Finder"
|
||||||
|
: "Explorer";
|
||||||
export const WORMHOLE_RPC_HOSTS =
|
export const WORMHOLE_RPC_HOSTS =
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? [
|
? [
|
||||||
|
@ -81,11 +103,19 @@ export const WORMHOLE_RPC_HOSTS =
|
||||||
? [
|
? [
|
||||||
"https://wormhole-v2-testnet-api.certus.one",
|
"https://wormhole-v2-testnet-api.certus.one",
|
||||||
"https://wormhole-v2-testnet-api.mcf.rocks",
|
"https://wormhole-v2-testnet-api.mcf.rocks",
|
||||||
"https://wormhole-v2-testnet-api.chainlayer.network"
|
"https://wormhole-v2-testnet-api.chainlayer.network",
|
||||||
]
|
]
|
||||||
: ["http://localhost:7071"];
|
: ["http://localhost:7071"];
|
||||||
export const ETH_NETWORK_CHAIN_ID =
|
export const ETH_NETWORK_CHAIN_ID =
|
||||||
CLUSTER === "mainnet" ? 1 : CLUSTER === "testnet" ? 5 : 1337;
|
CLUSTER === "mainnet" ? 1 : CLUSTER === "testnet" ? 5 : 1337;
|
||||||
|
export const BSC_NETWORK_CHAIN_ID =
|
||||||
|
CLUSTER === "mainnet" ? 56 : CLUSTER === "testnet" ? 97 : 1397;
|
||||||
|
export const getEvmChainId = (chainId: ChainId) =>
|
||||||
|
chainId === CHAIN_ID_ETH
|
||||||
|
? ETH_NETWORK_CHAIN_ID
|
||||||
|
: chainId === CHAIN_ID_BSC
|
||||||
|
? BSC_NETWORK_CHAIN_ID
|
||||||
|
: undefined;
|
||||||
export const SOLANA_HOST = process.env.REACT_APP_SOLANA_API_URL
|
export const SOLANA_HOST = process.env.REACT_APP_SOLANA_API_URL
|
||||||
? process.env.REACT_APP_SOLANA_API_URL
|
? process.env.REACT_APP_SOLANA_API_URL
|
||||||
: CLUSTER === "mainnet"
|
: CLUSTER === "mainnet"
|
||||||
|
@ -127,6 +157,27 @@ export const ETH_TOKEN_BRIDGE_ADDRESS = getAddress(
|
||||||
? "0xa6CDAddA6e4B6704705b065E01E52e2486c0FBf6"
|
? "0xa6CDAddA6e4B6704705b065E01E52e2486c0FBf6"
|
||||||
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
|
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
|
||||||
);
|
);
|
||||||
|
export const BSC_BRIDGE_ADDRESS = getAddress(
|
||||||
|
CLUSTER === "mainnet"
|
||||||
|
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
|
||||||
|
: CLUSTER === "testnet"
|
||||||
|
? "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" // TODO: test address
|
||||||
|
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
|
||||||
|
);
|
||||||
|
export const BSC_NFT_BRIDGE_ADDRESS = getAddress(
|
||||||
|
CLUSTER === "mainnet"
|
||||||
|
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
|
||||||
|
: CLUSTER === "testnet"
|
||||||
|
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
|
||||||
|
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
|
||||||
|
);
|
||||||
|
export const BSC_TOKEN_BRIDGE_ADDRESS = getAddress(
|
||||||
|
CLUSTER === "mainnet"
|
||||||
|
? "0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7"
|
||||||
|
: CLUSTER === "testnet"
|
||||||
|
? "0x0290FB167208Af455bB137780163b7B7a9a10C16" // TODO: test address
|
||||||
|
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
|
||||||
|
);
|
||||||
export const SOL_BRIDGE_ADDRESS =
|
export const SOL_BRIDGE_ADDRESS =
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"
|
? "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"
|
||||||
|
@ -151,24 +202,64 @@ export const SOL_CUSTODY_ADDRESS =
|
||||||
export const TERRA_TEST_TOKEN_ADDRESS =
|
export const TERRA_TEST_TOKEN_ADDRESS =
|
||||||
"terra13nkgqrfymug724h8pprpexqj9h629sa3ncw7sh";
|
"terra13nkgqrfymug724h8pprpexqj9h629sa3ncw7sh";
|
||||||
export const TERRA_BRIDGE_ADDRESS =
|
export const TERRA_BRIDGE_ADDRESS =
|
||||||
"terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5";
|
CLUSTER === "mainnet"
|
||||||
|
? "terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5"
|
||||||
|
: CLUSTER === "testnet"
|
||||||
|
? "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5"
|
||||||
|
: "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5";
|
||||||
export const TERRA_TOKEN_BRIDGE_ADDRESS =
|
export const TERRA_TOKEN_BRIDGE_ADDRESS =
|
||||||
"terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4";
|
CLUSTER === "mainnet"
|
||||||
|
? "terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf"
|
||||||
|
: CLUSTER === "testnet"
|
||||||
|
? "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4"
|
||||||
|
: "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4";
|
||||||
|
|
||||||
|
export const getBridgeAddressForChain = (chainId: ChainId) =>
|
||||||
|
chainId === CHAIN_ID_SOLANA
|
||||||
|
? SOL_BRIDGE_ADDRESS
|
||||||
|
: chainId === CHAIN_ID_ETH
|
||||||
|
? ETH_BRIDGE_ADDRESS
|
||||||
|
: chainId === CHAIN_ID_BSC
|
||||||
|
? BSC_BRIDGE_ADDRESS
|
||||||
|
: chainId === CHAIN_ID_TERRA
|
||||||
|
? TERRA_BRIDGE_ADDRESS
|
||||||
|
: "";
|
||||||
|
export const getNFTBridgeAddressForChain = (chainId: ChainId) =>
|
||||||
|
chainId === CHAIN_ID_SOLANA
|
||||||
|
? SOL_NFT_BRIDGE_ADDRESS
|
||||||
|
: chainId === CHAIN_ID_ETH
|
||||||
|
? ETH_NFT_BRIDGE_ADDRESS
|
||||||
|
: chainId === CHAIN_ID_BSC
|
||||||
|
? BSC_NFT_BRIDGE_ADDRESS
|
||||||
|
: "";
|
||||||
|
export const getTokenBridgeAddressForChain = (chainId: ChainId) =>
|
||||||
|
chainId === CHAIN_ID_SOLANA
|
||||||
|
? SOL_TOKEN_BRIDGE_ADDRESS
|
||||||
|
: chainId === CHAIN_ID_ETH
|
||||||
|
? ETH_TOKEN_BRIDGE_ADDRESS
|
||||||
|
: chainId === CHAIN_ID_BSC
|
||||||
|
? BSC_TOKEN_BRIDGE_ADDRESS
|
||||||
|
: chainId === CHAIN_ID_TERRA
|
||||||
|
? TERRA_TOKEN_BRIDGE_ADDRESS
|
||||||
|
: "";
|
||||||
|
|
||||||
export const COVALENT_API_KEY = process.env.REACT_APP_COVALENT_API_KEY
|
export const COVALENT_API_KEY = process.env.REACT_APP_COVALENT_API_KEY
|
||||||
? process.env.REACT_APP_COVALENT_API_KEY
|
? process.env.REACT_APP_COVALENT_API_KEY
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
export const COVALENT_ETHEREUM_MAINNET = "1";
|
export const COVALENT_ETHEREUM_MAINNET = "1";
|
||||||
|
export const COVALENT_BSC_MAINNET = "56";
|
||||||
export const COVALENT_GET_TOKENS_URL = (
|
export const COVALENT_GET_TOKENS_URL = (
|
||||||
chainId: ChainId,
|
chainId: ChainId,
|
||||||
walletAddress: string,
|
walletAddress: string,
|
||||||
nft?: boolean
|
nft?: boolean
|
||||||
) => {
|
) => {
|
||||||
let chainNum = "";
|
const chainNum =
|
||||||
if (chainId === CHAIN_ID_ETH) {
|
chainId === CHAIN_ID_ETH
|
||||||
chainNum = COVALENT_ETHEREUM_MAINNET;
|
? COVALENT_ETHEREUM_MAINNET
|
||||||
}
|
: chainId === CHAIN_ID_BSC
|
||||||
|
? COVALENT_BSC_MAINNET
|
||||||
|
: "";
|
||||||
// https://www.covalenthq.com/docs/api/#get-/v1/{chain_id}/address/{address}/balances_v2/
|
// https://www.covalenthq.com/docs/api/#get-/v1/{chain_id}/address/{address}/balances_v2/
|
||||||
return `https://api.covalenthq.com/v1/${chainNum}/address/${walletAddress}/balances_v2/?key=${COVALENT_API_KEY}${
|
return `https://api.covalenthq.com/v1/${chainNum}/address/${walletAddress}/balances_v2/?key=${COVALENT_API_KEY}${
|
||||||
nft ? "&nft=true" : ""
|
nft ? "&nft=true" : ""
|
||||||
|
@ -183,6 +274,14 @@ export const WETH_ADDRESS =
|
||||||
: "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
|
: "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
|
||||||
export const WETH_DECIMALS = 18;
|
export const WETH_DECIMALS = 18;
|
||||||
|
|
||||||
|
export const WBNB_ADDRESS =
|
||||||
|
CLUSTER === "mainnet"
|
||||||
|
? "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"
|
||||||
|
: CLUSTER === "testnet"
|
||||||
|
? ""
|
||||||
|
: "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
|
||||||
|
export const WBNB_DECIMALS = 18;
|
||||||
|
|
||||||
export const WORMHOLE_V1_ETH_ADDRESS =
|
export const WORMHOLE_V1_ETH_ADDRESS =
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "0xf92cD566Ea4864356C5491c177A430C222d7e678"
|
? "0xf92cD566Ea4864356C5491c177A430C222d7e678"
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import {
|
import {
|
||||||
|
ChainId,
|
||||||
|
CHAIN_ID_BSC,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
NFTImplementation,
|
NFTImplementation,
|
||||||
NFTImplementation__factory,
|
NFTImplementation__factory,
|
||||||
TokenImplementation,
|
TokenImplementation,
|
||||||
|
@ -11,6 +14,9 @@ import {
|
||||||
createParsedTokenAccount,
|
createParsedTokenAccount,
|
||||||
} from "../hooks/useGetSourceParsedTokenAccounts";
|
} from "../hooks/useGetSourceParsedTokenAccounts";
|
||||||
|
|
||||||
|
export const isEVMChain = (chainId: ChainId) =>
|
||||||
|
chainId === CHAIN_ID_ETH || chainId === CHAIN_ID_BSC;
|
||||||
|
|
||||||
//This is a valuable intermediate step to the parsed token account, as the token has metadata information on it.
|
//This is a valuable intermediate step to the parsed token account, as the token has metadata information on it.
|
||||||
export async function getEthereumToken(
|
export async function getEthereumToken(
|
||||||
tokenAddress: string,
|
tokenAddress: string,
|
||||||
|
|
|
@ -106,7 +106,7 @@ spec:
|
||||||
- --deterministic
|
- --deterministic
|
||||||
- --time="1970-01-01T00:00:00+00:00"
|
- --time="1970-01-01T00:00:00+00:00"
|
||||||
- --host=0.0.0.0
|
- --host=0.0.0.0
|
||||||
- -i 1337
|
- --chainId=1397
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8545
|
- containerPort: 8545
|
||||||
name: rpc
|
name: rpc
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Connection, PublicKey } from "@solana/web3.js";
|
||||||
import { BigNumber, ethers } from "ethers";
|
import { BigNumber, ethers } from "ethers";
|
||||||
import { arrayify, zeroPad } from "ethers/lib/utils";
|
import { arrayify, zeroPad } from "ethers/lib/utils";
|
||||||
import { TokenImplementation__factory } from "../ethers-contracts";
|
import { TokenImplementation__factory } from "../ethers-contracts";
|
||||||
import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "../utils";
|
import { ChainId, CHAIN_ID_SOLANA } from "../utils";
|
||||||
import { getIsWrappedAssetEth } from "./getIsWrappedAsset";
|
import { getIsWrappedAssetEth } from "./getIsWrappedAsset";
|
||||||
|
|
||||||
export interface WormholeWrappedNFTInfo {
|
export interface WormholeWrappedNFTInfo {
|
||||||
|
@ -23,7 +23,8 @@ export async function getOriginalAssetEth(
|
||||||
tokenBridgeAddress: string,
|
tokenBridgeAddress: string,
|
||||||
provider: ethers.providers.Web3Provider,
|
provider: ethers.providers.Web3Provider,
|
||||||
wrappedAddress: string,
|
wrappedAddress: string,
|
||||||
tokenId: string
|
tokenId: string,
|
||||||
|
lookupChainId: ChainId
|
||||||
): Promise<WormholeWrappedNFTInfo> {
|
): Promise<WormholeWrappedNFTInfo> {
|
||||||
const isWrapped = await getIsWrappedAssetEth(
|
const isWrapped = await getIsWrappedAssetEth(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
|
@ -49,7 +50,7 @@ export async function getOriginalAssetEth(
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
isWrapped: false,
|
isWrapped: false,
|
||||||
chainId: CHAIN_ID_ETH,
|
chainId: lookupChainId,
|
||||||
assetAddress: zeroPad(arrayify(wrappedAddress), 32),
|
assetAddress: zeroPad(arrayify(wrappedAddress), 32),
|
||||||
tokenId,
|
tokenId,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
import { Connection, PublicKey } from "@solana/web3.js";
|
import { Connection, PublicKey } from "@solana/web3.js";
|
||||||
|
import { LCDClient } from "@terra-money/terra.js";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { arrayify, zeroPad } from "ethers/lib/utils";
|
import { arrayify, zeroPad } from "ethers/lib/utils";
|
||||||
import { TokenImplementation__factory } from "../ethers-contracts";
|
import { TokenImplementation__factory } from "../ethers-contracts";
|
||||||
import {
|
|
||||||
ChainId,
|
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
|
||||||
CHAIN_ID_TERRA,
|
|
||||||
} from "../utils";
|
|
||||||
import { getIsWrappedAssetEth } from "./getIsWrappedAsset";
|
|
||||||
import { LCDClient } from "@terra-money/terra.js";
|
|
||||||
import { buildNativeId, canonicalAddress, isNativeDenom } from "../terra";
|
import { buildNativeId, canonicalAddress, isNativeDenom } from "../terra";
|
||||||
|
import { ChainId, CHAIN_ID_SOLANA, CHAIN_ID_TERRA } from "../utils";
|
||||||
|
import { getIsWrappedAssetEth } from "./getIsWrappedAsset";
|
||||||
|
|
||||||
export interface WormholeWrappedInfo {
|
export interface WormholeWrappedInfo {
|
||||||
isWrapped: boolean;
|
isWrapped: boolean;
|
||||||
|
@ -28,7 +23,8 @@ export interface WormholeWrappedInfo {
|
||||||
export async function getOriginalAssetEth(
|
export async function getOriginalAssetEth(
|
||||||
tokenBridgeAddress: string,
|
tokenBridgeAddress: string,
|
||||||
provider: ethers.providers.Web3Provider,
|
provider: ethers.providers.Web3Provider,
|
||||||
wrappedAddress: string
|
wrappedAddress: string,
|
||||||
|
lookupChainId: ChainId
|
||||||
): Promise<WormholeWrappedInfo> {
|
): Promise<WormholeWrappedInfo> {
|
||||||
const isWrapped = await getIsWrappedAssetEth(
|
const isWrapped = await getIsWrappedAssetEth(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
|
@ -50,7 +46,7 @@ export async function getOriginalAssetEth(
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
isWrapped: false,
|
isWrapped: false,
|
||||||
chainId: CHAIN_ID_ETH,
|
chainId: lookupChainId,
|
||||||
assetAddress: zeroPad(arrayify(wrappedAddress), 32),
|
assetAddress: zeroPad(arrayify(wrappedAddress), 32),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {
|
import {
|
||||||
ChainId,
|
ChainId,
|
||||||
|
CHAIN_ID_BSC,
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
|
@ -21,7 +22,7 @@ export const hexToNativeString = (h: string | undefined, c: ChainId) => {
|
||||||
? undefined
|
? undefined
|
||||||
: c === CHAIN_ID_SOLANA
|
: c === CHAIN_ID_SOLANA
|
||||||
? new PublicKey(hexToUint8Array(h)).toString()
|
? new PublicKey(hexToUint8Array(h)).toString()
|
||||||
: c === CHAIN_ID_ETH
|
: c === CHAIN_ID_ETH || c === CHAIN_ID_BSC
|
||||||
? hexZeroPad(hexValue(hexToUint8Array(h)), 20)
|
? hexZeroPad(hexValue(hexToUint8Array(h)), 20)
|
||||||
: c === CHAIN_ID_TERRA
|
: c === CHAIN_ID_TERRA
|
||||||
? isHexNativeTerra(h)
|
? isHexNativeTerra(h)
|
||||||
|
|
Loading…
Reference in New Issue