bridge_ui: featured markets
Change-Id: I72a48fd3f1ff674d64aad58c9a0020585d234c82
This commit is contained in:
parent
54ebc6481f
commit
793a4b4f5f
|
@ -146,36 +146,16 @@ export default function EvmTokenPicker(
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//For now, just swallow this one.
|
//For now, just swallow this one.
|
||||||
}
|
}
|
||||||
let newAccount = null;
|
|
||||||
try {
|
|
||||||
//Covalent balances tend to be stale, so we make an attempt to correct it at selection time.
|
|
||||||
if (!account.isNativeAsset) {
|
|
||||||
newAccount = await getAddress(account.mintKey, account.tokenId);
|
|
||||||
newAccount = { ...account, ...newAccount }; //We spread over the old account so we don't lose the logo, uri, or other useful info we got from covalent.
|
|
||||||
} else {
|
|
||||||
newAccount = account;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
//swallow
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
if (!newAccount) {
|
|
||||||
//Must reject otherwise downstream checks relying on the balance may fail.
|
|
||||||
//An error is thrown so that the code above us will display the message.
|
|
||||||
throw new Error(
|
|
||||||
"Unable to retrieve required information about this token. Ensure your wallet is connected, then refresh the list."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const migration = isMigrationEligible(account.publicKey);
|
const migration = isMigrationEligible(account.publicKey);
|
||||||
if (v1 === true && !migration) {
|
if (v1 === true && !migration) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Wormhole v1 assets cannot be transferred with this bridge."
|
"Wormhole v1 assets cannot be transferred with this bridge."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
onChange(newAccount);
|
onChange(account);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
[chainId, onChange, provider, isMigrationEligible, getAddress]
|
[chainId, onChange, provider, isMigrationEligible]
|
||||||
);
|
);
|
||||||
|
|
||||||
const RenderComp = useCallback(
|
const RenderComp = useCallback(
|
||||||
|
|
|
@ -7,7 +7,9 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
|
Divider,
|
||||||
IconButton,
|
IconButton,
|
||||||
|
Link,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
makeStyles,
|
makeStyles,
|
||||||
|
@ -15,11 +17,16 @@ import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
|
import { InfoOutlined, Launch } from "@material-ui/icons";
|
||||||
import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown";
|
import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown";
|
||||||
import RefreshIcon from "@material-ui/icons/Refresh";
|
import RefreshIcon from "@material-ui/icons/Refresh";
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import useMarketsMap from "../../hooks/useMarketsMap";
|
||||||
import { NFTParsedTokenAccount } from "../../store/nftSlice";
|
import { NFTParsedTokenAccount } from "../../store/nftSlice";
|
||||||
|
import { selectTransferTargetChain } from "../../store/selectors";
|
||||||
|
import { AVAILABLE_MARKETS_URL, CHAINS_BY_ID } from "../../utils/consts";
|
||||||
import { shortenAddress } from "../../utils/solana";
|
import { shortenAddress } from "../../utils/solana";
|
||||||
import NFTViewer from "./NFTViewer";
|
import NFTViewer from "./NFTViewer";
|
||||||
|
|
||||||
|
@ -62,6 +69,11 @@ const useStyles = makeStyles((theme) =>
|
||||||
"&$tokenImageContainer": {
|
"&$tokenImageContainer": {
|
||||||
maxWidth: 40,
|
maxWidth: 40,
|
||||||
},
|
},
|
||||||
|
"&$tokenMarketsList": {
|
||||||
|
marginTop: theme.spacing(-0.5),
|
||||||
|
marginLeft: 0,
|
||||||
|
flexBasis: "100%",
|
||||||
|
},
|
||||||
"&:last-child": {
|
"&:last-child": {
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
},
|
},
|
||||||
|
@ -78,6 +90,15 @@ const useStyles = makeStyles((theme) =>
|
||||||
tokenImage: {
|
tokenImage: {
|
||||||
maxHeight: "2.5rem", //Eyeballing this based off the text size
|
maxHeight: "2.5rem", //Eyeballing this based off the text size
|
||||||
},
|
},
|
||||||
|
tokenMarketsList: {
|
||||||
|
order: 1,
|
||||||
|
textAlign: "left",
|
||||||
|
width: "100%",
|
||||||
|
"& > .MuiButton-root": {
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
migrationAlert: {
|
migrationAlert: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
"& .MuiAlert-message": {
|
"& .MuiAlert-message": {
|
||||||
|
@ -107,12 +128,17 @@ export const balancePretty = (uiString: string) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const noClickThrough = (e: any) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
export const BasicAccountRender = (
|
export const BasicAccountRender = (
|
||||||
account: NFTParsedTokenAccount,
|
account: MarketParsedTokenAccount,
|
||||||
isMigrationEligible: (address: string) => boolean,
|
isMigrationEligible: (address: string) => boolean,
|
||||||
nft: boolean,
|
nft: boolean,
|
||||||
displayBalance?: (account: NFTParsedTokenAccount) => boolean
|
displayBalance?: (account: NFTParsedTokenAccount) => boolean
|
||||||
) => {
|
) => {
|
||||||
|
const { data: marketsData } = useMarketsMap(false);
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const mintPrettyString = shortenAddress(account.mintKey);
|
const mintPrettyString = shortenAddress(account.mintKey);
|
||||||
const uri = nft ? account.image_256 : account.logo || account.uri;
|
const uri = nft ? account.image_256 : account.logo || account.uri;
|
||||||
|
@ -139,6 +165,27 @@ export const BasicAccountRender = (
|
||||||
|
|
||||||
const tokenContent = (
|
const tokenContent = (
|
||||||
<div className={classes.tokenOverviewContainer}>
|
<div className={classes.tokenOverviewContainer}>
|
||||||
|
{account.markets ? (
|
||||||
|
<div className={classes.tokenMarketsList}>
|
||||||
|
{account.markets.map((market) =>
|
||||||
|
marketsData?.markets?.[market] ? (
|
||||||
|
<Button
|
||||||
|
key={market}
|
||||||
|
size="small"
|
||||||
|
variant="outlined"
|
||||||
|
color="secondary"
|
||||||
|
endIcon={<Launch />}
|
||||||
|
href={marketsData.markets[market].link}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
onClick={noClickThrough}
|
||||||
|
>
|
||||||
|
{marketsData.markets[market].name}
|
||||||
|
</Button>
|
||||||
|
) : null
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<div className={classes.tokenImageContainer}>
|
<div className={classes.tokenImageContainer}>
|
||||||
{uri && <img alt="" className={classes.tokenImage} src={uri} />}
|
{uri && <img alt="" className={classes.tokenImage} src={uri} />}
|
||||||
</div>
|
</div>
|
||||||
|
@ -185,6 +232,10 @@ export const BasicAccountRender = (
|
||||||
: tokenContent;
|
: tokenContent;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface MarketParsedTokenAccount extends NFTParsedTokenAccount {
|
||||||
|
markets?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export default function TokenPicker({
|
export default function TokenPicker({
|
||||||
value,
|
value,
|
||||||
options,
|
options,
|
||||||
|
@ -229,6 +280,9 @@ export default function TokenPicker({
|
||||||
const [dialogIsOpen, setDialogIsOpen] = useState(false);
|
const [dialogIsOpen, setDialogIsOpen] = useState(false);
|
||||||
const [selectionError, setSelectionError] = useState("");
|
const [selectionError, setSelectionError] = useState("");
|
||||||
|
|
||||||
|
const targetChain = useSelector(selectTransferTargetChain);
|
||||||
|
const { data: marketsData } = useMarketsMap(true);
|
||||||
|
|
||||||
const openDialog = useCallback(() => {
|
const openDialog = useCallback(() => {
|
||||||
setHolderString("");
|
setHolderString("");
|
||||||
setSelectionError("");
|
setSelectionError("");
|
||||||
|
@ -242,16 +296,31 @@ export default function TokenPicker({
|
||||||
const handleSelectOption = useCallback(
|
const handleSelectOption = useCallback(
|
||||||
async (option: NFTParsedTokenAccount) => {
|
async (option: NFTParsedTokenAccount) => {
|
||||||
setSelectionError("");
|
setSelectionError("");
|
||||||
onChange(option).then(
|
let newOption = null;
|
||||||
() => {
|
try {
|
||||||
closeDialog();
|
//Covalent balances tend to be stale, so we make an attempt to correct it at selection time.
|
||||||
},
|
if (getAddress && !option.isNativeAsset) {
|
||||||
(error) => {
|
newOption = await getAddress(option.mintKey, option.tokenId);
|
||||||
setSelectionError(error?.message || "Error verifying the token.");
|
newOption = {
|
||||||
|
...option,
|
||||||
|
...newOption,
|
||||||
|
// keep logo and uri from covalent / market list / etc (otherwise would be overwritten by undefined)
|
||||||
|
logo: option.logo || newOption.logo,
|
||||||
|
uri: option.uri || newOption.uri,
|
||||||
|
} as NFTParsedTokenAccount;
|
||||||
|
} else {
|
||||||
|
newOption = option;
|
||||||
}
|
}
|
||||||
);
|
await onChange(newOption);
|
||||||
|
closeDialog();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
setSelectionError(
|
||||||
|
"Unable to retrieve required information about this token. Ensure your wallet is connected, then refresh the list."
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[onChange, closeDialog]
|
[getAddress, onChange, closeDialog]
|
||||||
);
|
);
|
||||||
|
|
||||||
const resetAccountsWrapper = useCallback(() => {
|
const resetAccountsWrapper = useCallback(() => {
|
||||||
|
@ -261,8 +330,8 @@ export default function TokenPicker({
|
||||||
resetAccounts && resetAccounts();
|
resetAccounts && resetAccounts();
|
||||||
}, [resetAccounts]);
|
}, [resetAccounts]);
|
||||||
|
|
||||||
const filteredOptions = useMemo(() => {
|
const searchFilter = useCallback(
|
||||||
return options.filter((option: NFTParsedTokenAccount) => {
|
(option: NFTParsedTokenAccount) => {
|
||||||
if (!holderString) {
|
if (!holderString) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -277,8 +346,61 @@ export default function TokenPicker({
|
||||||
).toLowerCase();
|
).toLowerCase();
|
||||||
const searchString = holderString.toLowerCase();
|
const searchString = holderString.toLowerCase();
|
||||||
return optionString.includes(searchString);
|
return optionString.includes(searchString);
|
||||||
});
|
},
|
||||||
}, [holderString, options]);
|
[holderString]
|
||||||
|
);
|
||||||
|
|
||||||
|
const marketChainTokens = marketsData?.tokens?.[chainId];
|
||||||
|
const featuredMarkets = marketsData?.tokenMarkets?.[chainId]?.[targetChain];
|
||||||
|
|
||||||
|
const featuredOptions = useMemo(() => {
|
||||||
|
// only tokens have featured markets
|
||||||
|
if (!nft && featuredMarkets) {
|
||||||
|
const ownedMarketTokens = options
|
||||||
|
.filter(
|
||||||
|
(option: NFTParsedTokenAccount) => featuredMarkets?.[option.mintKey]
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
(option) =>
|
||||||
|
({
|
||||||
|
...option,
|
||||||
|
markets: featuredMarkets[option.mintKey].markets,
|
||||||
|
} as MarketParsedTokenAccount)
|
||||||
|
);
|
||||||
|
return [
|
||||||
|
...ownedMarketTokens,
|
||||||
|
...Object.keys(featuredMarkets)
|
||||||
|
.filter(
|
||||||
|
(mintKey) =>
|
||||||
|
!ownedMarketTokens.find((option) => option.mintKey === mintKey)
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
(mintKey) =>
|
||||||
|
({
|
||||||
|
amount: "0",
|
||||||
|
decimals: 0,
|
||||||
|
markets: featuredMarkets[mintKey].markets,
|
||||||
|
mintKey,
|
||||||
|
publicKey: "",
|
||||||
|
uiAmount: 0,
|
||||||
|
uiAmountString: "0", // if we can't look up by address, we can select the market that isn't in the list of holdings, but can't proceed since the balance will be 0
|
||||||
|
symbol: marketChainTokens?.[mintKey]?.symbol,
|
||||||
|
logo: marketChainTokens?.[mintKey]?.logo,
|
||||||
|
} as MarketParsedTokenAccount)
|
||||||
|
),
|
||||||
|
].filter(searchFilter);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [nft, marketChainTokens, featuredMarkets, options, searchFilter]);
|
||||||
|
|
||||||
|
const nonFeaturedOptions = useMemo(() => {
|
||||||
|
return options.filter(
|
||||||
|
(option: NFTParsedTokenAccount) =>
|
||||||
|
searchFilter(option) &&
|
||||||
|
// only tokens have featured markets
|
||||||
|
(nft || !featuredMarkets?.[option.mintKey])
|
||||||
|
);
|
||||||
|
}, [nft, options, featuredMarkets, searchFilter]);
|
||||||
|
|
||||||
const localFind = useCallback(
|
const localFind = useCallback(
|
||||||
(address: string, tokenIdHolderString: string) => {
|
(address: string, tokenIdHolderString: string) => {
|
||||||
|
@ -383,6 +505,17 @@ export default function TokenPicker({
|
||||||
</div>
|
</div>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent className={classes.dialogContent}>
|
<DialogContent className={classes.dialogContent}>
|
||||||
|
<Alert severity="info">
|
||||||
|
You should always check for markets and liquidity before sending
|
||||||
|
tokens.{" "}
|
||||||
|
<Link
|
||||||
|
href={AVAILABLE_MARKETS_URL}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Click here to see available markets for wrapped tokens.
|
||||||
|
</Link>
|
||||||
|
</Alert>
|
||||||
<TextField
|
<TextField
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
label="Search name or paste address"
|
label="Search name or paste address"
|
||||||
|
@ -405,11 +538,52 @@ export default function TokenPicker({
|
||||||
localLoader
|
localLoader
|
||||||
) : loadingError || selectionError ? (
|
) : loadingError || selectionError ? (
|
||||||
displayLocalError
|
displayLocalError
|
||||||
) : filteredOptions.length ? (
|
) : (
|
||||||
<List className={classes.tokenList}>
|
<List component="div" className={classes.tokenList}>
|
||||||
{filteredOptions.map((option) => {
|
{featuredOptions.length ? (
|
||||||
|
<>
|
||||||
|
<Typography variant="subtitle2" gutterBottom>
|
||||||
|
Featured {CHAINS_BY_ID[chainId].name} >{" "}
|
||||||
|
{CHAINS_BY_ID[targetChain].name} markets{" "}
|
||||||
|
<Tooltip
|
||||||
|
title={`Markets for these ${CHAINS_BY_ID[chainId].name} tokens exist for the corresponding tokens on ${CHAINS_BY_ID[targetChain].name}`}
|
||||||
|
>
|
||||||
|
<InfoOutlined
|
||||||
|
fontSize="small"
|
||||||
|
style={{ verticalAlign: "text-bottom" }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Typography>
|
||||||
|
{featuredOptions.map((option) => {
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
component="div"
|
||||||
|
button
|
||||||
|
onClick={() => handleSelectOption(option)}
|
||||||
|
key={
|
||||||
|
option.publicKey +
|
||||||
|
option.mintKey +
|
||||||
|
(option.tokenId || "")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<RenderOption account={option} />
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{nonFeaturedOptions.length ? (
|
||||||
|
<>
|
||||||
|
<Divider style={{ marginTop: 8, marginBottom: 16 }} />
|
||||||
|
<Typography variant="subtitle2" gutterBottom>
|
||||||
|
Other Assets
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{nonFeaturedOptions.map((option) => {
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
|
component="div"
|
||||||
button
|
button
|
||||||
onClick={() => handleSelectOption(option)}
|
onClick={() => handleSelectOption(option)}
|
||||||
key={
|
key={
|
||||||
|
@ -420,11 +594,12 @@ export default function TokenPicker({
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{featuredOptions.length || nonFeaturedOptions.length ? null : (
|
||||||
|
<div className={classes.alignCenter}>
|
||||||
|
<Typography>No results found</Typography>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</List>
|
</List>
|
||||||
) : (
|
|
||||||
<div className={classes.alignCenter}>
|
|
||||||
<Typography>No results found</Typography>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
|
@ -4,12 +4,12 @@ import {
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { getAddress } from "@ethersproject/address";
|
import { getAddress } from "@ethersproject/address";
|
||||||
import { Button, makeStyles } from "@material-ui/core";
|
import { Button, makeStyles, Typography } from "@material-ui/core";
|
||||||
import { Link } from "react-router-dom";
|
import { ArrowForward, VerifiedUser } from "@material-ui/icons";
|
||||||
import { VerifiedUser } from "@material-ui/icons";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useCallback } from "react";
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useHistory } from "react-router";
|
import { useHistory } from "react-router";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
||||||
import {
|
import {
|
||||||
selectTransferAmount,
|
selectTransferAmount,
|
||||||
|
@ -19,11 +19,13 @@ import {
|
||||||
selectTransferSourceChain,
|
selectTransferSourceChain,
|
||||||
selectTransferSourceError,
|
selectTransferSourceError,
|
||||||
selectTransferSourceParsedTokenAccount,
|
selectTransferSourceParsedTokenAccount,
|
||||||
|
selectTransferTargetChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import {
|
import {
|
||||||
incrementStep,
|
incrementStep,
|
||||||
setAmount,
|
setAmount,
|
||||||
setSourceChain,
|
setSourceChain,
|
||||||
|
setTargetChain,
|
||||||
} from "../../store/transferSlice";
|
} from "../../store/transferSlice";
|
||||||
import {
|
import {
|
||||||
BSC_MIGRATION_ASSET_MAP,
|
BSC_MIGRATION_ASSET_MAP,
|
||||||
|
@ -40,6 +42,24 @@ import StepDescription from "../StepDescription";
|
||||||
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
chainSelectWrapper: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
[theme.breakpoints.down("sm")]: {
|
||||||
|
flexDirection: "column",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
chainSelectContainer: {
|
||||||
|
flexBasis: "100%",
|
||||||
|
[theme.breakpoints.down("sm")]: {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
chainSelectArrow: {
|
||||||
|
position: "relative",
|
||||||
|
top: "12px",
|
||||||
|
[theme.breakpoints.down("sm")]: { transform: "rotate(90deg)" },
|
||||||
|
},
|
||||||
transferField: {
|
transferField: {
|
||||||
marginTop: theme.spacing(5),
|
marginTop: theme.spacing(5),
|
||||||
},
|
},
|
||||||
|
@ -50,6 +70,11 @@ function Source() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const sourceChain = useSelector(selectTransferSourceChain);
|
const sourceChain = useSelector(selectTransferSourceChain);
|
||||||
|
const targetChain = useSelector(selectTransferTargetChain);
|
||||||
|
const targetChainOptions = useMemo(
|
||||||
|
() => CHAINS.filter((c) => c.id !== sourceChain),
|
||||||
|
[sourceChain]
|
||||||
|
);
|
||||||
const parsedTokenAccount = useSelector(
|
const parsedTokenAccount = useSelector(
|
||||||
selectTransferSourceParsedTokenAccount
|
selectTransferSourceParsedTokenAccount
|
||||||
);
|
);
|
||||||
|
@ -91,6 +116,12 @@ function Source() {
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
const handleTargetChange = useCallback(
|
||||||
|
(event) => {
|
||||||
|
dispatch(setTargetChain(event.target.value));
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
const handleAmountChange = useCallback(
|
const handleAmountChange = useCallback(
|
||||||
(event) => {
|
(event) => {
|
||||||
dispatch(setAmount(event.target.value));
|
dispatch(setAmount(event.target.value));
|
||||||
|
@ -124,15 +155,35 @@ function Source() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</StepDescription>
|
</StepDescription>
|
||||||
<ChainSelect
|
<div className={classes.chainSelectWrapper}>
|
||||||
select
|
<div className={classes.chainSelectContainer}>
|
||||||
variant="outlined"
|
<Typography variant="caption">Source</Typography>
|
||||||
fullWidth
|
<ChainSelect
|
||||||
value={sourceChain}
|
select
|
||||||
onChange={handleSourceChange}
|
variant="outlined"
|
||||||
disabled={shouldLockFields}
|
fullWidth
|
||||||
chains={CHAINS}
|
value={sourceChain}
|
||||||
/>
|
onChange={handleSourceChange}
|
||||||
|
disabled={shouldLockFields}
|
||||||
|
chains={CHAINS}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={classes.chainSelectArrow}>
|
||||||
|
<ArrowForward style={{ margin: "0px 8px" }} />
|
||||||
|
</div>
|
||||||
|
<div className={classes.chainSelectContainer}>
|
||||||
|
<Typography variant="caption">Target</Typography>
|
||||||
|
<ChainSelect
|
||||||
|
variant="outlined"
|
||||||
|
select
|
||||||
|
fullWidth
|
||||||
|
value={targetChain}
|
||||||
|
onChange={handleTargetChange}
|
||||||
|
disabled={shouldLockFields}
|
||||||
|
chains={targetChainOptions}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<KeyAndBalance chainId={sourceChain} />
|
<KeyAndBalance chainId={sourceChain} />
|
||||||
{isReady || uiAmountString ? (
|
{isReady || uiAmountString ? (
|
||||||
<div className={classes.transferField}>
|
<div className={classes.transferField}>
|
||||||
|
|
|
@ -125,7 +125,7 @@ function Target() {
|
||||||
fullWidth
|
fullWidth
|
||||||
value={targetChain}
|
value={targetChain}
|
||||||
onChange={handleTargetChange}
|
onChange={handleTargetChange}
|
||||||
disabled={shouldLockFields}
|
disabled={true}
|
||||||
chains={chains}
|
chains={chains}
|
||||||
/>
|
/>
|
||||||
<KeyAndBalance chainId={targetChain} />
|
<KeyAndBalance chainId={targetChain} />
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { ChainId } from "@certusone/wormhole-sdk";
|
||||||
|
import { Dispatch } from "@reduxjs/toolkit";
|
||||||
|
import axios from "axios";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { DataWrapper } from "../store/helpers";
|
||||||
|
import { selectMarketsMap } from "../store/selectors";
|
||||||
|
import {
|
||||||
|
errorMarketsMap,
|
||||||
|
fetchMarketsMap,
|
||||||
|
receiveMarketsMap,
|
||||||
|
} from "../store/tokenSlice";
|
||||||
|
import { FEATURED_MARKETS_JSON_URL } from "../utils/consts";
|
||||||
|
|
||||||
|
export type MarketsMap = {
|
||||||
|
markets?: {
|
||||||
|
[index: string]: {
|
||||||
|
name: string;
|
||||||
|
link: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
tokens?: {
|
||||||
|
[key in ChainId]?: {
|
||||||
|
[index: string]: {
|
||||||
|
symbol: string;
|
||||||
|
logo: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
tokenMarkets?: {
|
||||||
|
[key in ChainId]?: {
|
||||||
|
[key in ChainId]?: {
|
||||||
|
[index: string]: {
|
||||||
|
symbol: string;
|
||||||
|
logo: string;
|
||||||
|
markets: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const useMarketsMap = (shouldFire: boolean): DataWrapper<MarketsMap> => {
|
||||||
|
const marketsMap = useSelector(selectMarketsMap);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const internalShouldFire =
|
||||||
|
shouldFire &&
|
||||||
|
(marketsMap.data === undefined ||
|
||||||
|
(marketsMap.data === null && !marketsMap.isFetching));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (internalShouldFire) {
|
||||||
|
getMarketsMap(dispatch);
|
||||||
|
}
|
||||||
|
}, [internalShouldFire, dispatch]);
|
||||||
|
|
||||||
|
return marketsMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMarketsMap = (dispatch: Dispatch) => {
|
||||||
|
dispatch(fetchMarketsMap());
|
||||||
|
axios.get(FEATURED_MARKETS_JSON_URL).then(
|
||||||
|
(response) => {
|
||||||
|
dispatch(receiveMarketsMap(response.data as MarketsMap));
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
dispatch(errorMarketsMap("Failed to retrieve the Terra Token List."));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useMarketsMap;
|
|
@ -292,3 +292,7 @@ export const selectSolanaTokenMap = (state: RootState) => {
|
||||||
export const selectTerraTokenMap = (state: RootState) => {
|
export const selectTerraTokenMap = (state: RootState) => {
|
||||||
return state.tokens.terraTokenMap;
|
return state.tokens.terraTokenMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const selectMarketsMap = (state: RootState) => {
|
||||||
|
return state.tokens.marketsMap;
|
||||||
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import { TokenInfo } from "@solana/spl-token-registry";
|
import { TokenInfo } from "@solana/spl-token-registry";
|
||||||
import { TerraTokenMap } from "../hooks/useTerraTokenMap";
|
import { TerraTokenMap } from "../hooks/useTerraTokenMap";
|
||||||
|
import { MarketsMap } from "../hooks/useMarketsMap";
|
||||||
import {
|
import {
|
||||||
DataWrapper,
|
DataWrapper,
|
||||||
errorDataWrapper,
|
errorDataWrapper,
|
||||||
|
@ -12,11 +13,13 @@ import {
|
||||||
export interface TokenMetadataState {
|
export interface TokenMetadataState {
|
||||||
solanaTokenMap: DataWrapper<TokenInfo[]>;
|
solanaTokenMap: DataWrapper<TokenInfo[]>;
|
||||||
terraTokenMap: DataWrapper<TerraTokenMap>; //TODO make a decent type for this.
|
terraTokenMap: DataWrapper<TerraTokenMap>; //TODO make a decent type for this.
|
||||||
|
marketsMap: DataWrapper<MarketsMap>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: TokenMetadataState = {
|
const initialState: TokenMetadataState = {
|
||||||
solanaTokenMap: getEmptyDataWrapper(),
|
solanaTokenMap: getEmptyDataWrapper(),
|
||||||
terraTokenMap: getEmptyDataWrapper(),
|
terraTokenMap: getEmptyDataWrapper(),
|
||||||
|
marketsMap: getEmptyDataWrapper(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const tokenSlice = createSlice({
|
export const tokenSlice = createSlice({
|
||||||
|
@ -43,6 +46,16 @@ export const tokenSlice = createSlice({
|
||||||
state.terraTokenMap = errorDataWrapper(action.payload);
|
state.terraTokenMap = errorDataWrapper(action.payload);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
receiveMarketsMap: (state, action: PayloadAction<MarketsMap>) => {
|
||||||
|
state.marketsMap = receiveDataWrapper(action.payload);
|
||||||
|
},
|
||||||
|
fetchMarketsMap: (state) => {
|
||||||
|
state.marketsMap = fetchDataWrapper();
|
||||||
|
},
|
||||||
|
errorMarketsMap: (state, action: PayloadAction<string>) => {
|
||||||
|
state.marketsMap = errorDataWrapper(action.payload);
|
||||||
|
},
|
||||||
|
|
||||||
reset: () => initialState,
|
reset: () => initialState,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -54,6 +67,9 @@ export const {
|
||||||
receiveTerraTokenMap,
|
receiveTerraTokenMap,
|
||||||
fetchTerraTokenMap,
|
fetchTerraTokenMap,
|
||||||
errorTerraTokenMap,
|
errorTerraTokenMap,
|
||||||
|
receiveMarketsMap,
|
||||||
|
fetchMarketsMap,
|
||||||
|
errorMarketsMap,
|
||||||
reset,
|
reset,
|
||||||
} = tokenSlice.actions;
|
} = tokenSlice.actions;
|
||||||
|
|
||||||
|
|
|
@ -136,9 +136,7 @@ export const WORMHOLE_RPC_HOSTS =
|
||||||
"https://wormhole-v2-mainnet-api.chainlayer.network",
|
"https://wormhole-v2-mainnet-api.chainlayer.network",
|
||||||
]
|
]
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? [
|
? ["https://wormhole-v2-testnet-api.certus.one"]
|
||||||
"https://wormhole-v2-testnet-api.certus.one",
|
|
||||||
]
|
|
||||||
: ["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;
|
||||||
|
@ -159,7 +157,7 @@ export const SOLANA_HOST = process.env.REACT_APP_SOLANA_API_URL
|
||||||
: CLUSTER === "mainnet"
|
: CLUSTER === "mainnet"
|
||||||
? clusterApiUrl("mainnet-beta")
|
? clusterApiUrl("mainnet-beta")
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? clusterApiUrl("testnet")
|
? clusterApiUrl("devnet")
|
||||||
: "http://localhost:8899";
|
: "http://localhost:8899";
|
||||||
|
|
||||||
export const TERRA_HOST =
|
export const TERRA_HOST =
|
||||||
|
@ -184,82 +182,82 @@ export const ETH_BRIDGE_ADDRESS = getAddress(
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
|
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "0x44F3e7c20850B3B5f3031114726A9240911D912a"
|
? "0xC0231E0957596A90004119f4254aff364f6f1002"
|
||||||
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
|
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
|
||||||
);
|
);
|
||||||
export const ETH_NFT_BRIDGE_ADDRESS = getAddress(
|
export const ETH_NFT_BRIDGE_ADDRESS = getAddress(
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE"
|
? "0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
|
? "0x5B78d166Fc3C2c99783B60b959dC35E316EBB5e7"
|
||||||
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
|
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
|
||||||
);
|
);
|
||||||
export const ETH_TOKEN_BRIDGE_ADDRESS = getAddress(
|
export const ETH_TOKEN_BRIDGE_ADDRESS = getAddress(
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
|
? "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "0xa6CDAddA6e4B6704705b065E01E52e2486c0FBf6"
|
? "0xc59072C84ECD13DbF30856021C0a33868121Cb9d"
|
||||||
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
|
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
|
||||||
);
|
);
|
||||||
export const BSC_BRIDGE_ADDRESS = getAddress(
|
export const BSC_BRIDGE_ADDRESS = getAddress(
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
|
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" // TODO: test address
|
? "0x61D9309dC73CcAC3c639aeC497A11320C5A72074"
|
||||||
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
|
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
|
||||||
);
|
);
|
||||||
export const BSC_NFT_BRIDGE_ADDRESS = getAddress(
|
export const BSC_NFT_BRIDGE_ADDRESS = getAddress(
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
|
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
|
? "0x55A525D72f4b08762991e4ECDB1aDb5Ab55dFf37"
|
||||||
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
|
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
|
||||||
);
|
);
|
||||||
export const BSC_TOKEN_BRIDGE_ADDRESS = getAddress(
|
export const BSC_TOKEN_BRIDGE_ADDRESS = getAddress(
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7"
|
? "0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "0x0290FB167208Af455bB137780163b7B7a9a10C16" // TODO: test address
|
? "0xC708B76f0C28040A0f852DbacB26375eDB071c1D"
|
||||||
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
|
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
|
||||||
);
|
);
|
||||||
export const POLYGON_BRIDGE_ADDRESS = getAddress(
|
export const POLYGON_BRIDGE_ADDRESS = getAddress(
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7"
|
? "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" // TODO: test address
|
? "0x61D9309dC73CcAC3c639aeC497A11320C5A72074"
|
||||||
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
|
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
|
||||||
);
|
);
|
||||||
export const POLYGON_NFT_BRIDGE_ADDRESS = getAddress(
|
export const POLYGON_NFT_BRIDGE_ADDRESS = getAddress(
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "0x90BBd86a6Fe93D3bc3ed6335935447E75fAb7fCf"
|
? "0x90BBd86a6Fe93D3bc3ed6335935447E75fAb7fCf"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
|
? "0x55A525D72f4b08762991e4ECDB1aDb5Ab55dFf37"
|
||||||
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
|
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
|
||||||
);
|
);
|
||||||
export const POLYGON_TOKEN_BRIDGE_ADDRESS = getAddress(
|
export const POLYGON_TOKEN_BRIDGE_ADDRESS = getAddress(
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
|
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "0x0290FB167208Af455bB137780163b7B7a9a10C16" // TODO: test address
|
? "0xC708B76f0C28040A0f852DbacB26375eDB071c1D"
|
||||||
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
|
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
|
||||||
);
|
);
|
||||||
export const SOL_BRIDGE_ADDRESS =
|
export const SOL_BRIDGE_ADDRESS =
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"
|
? "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "Brdguy7BmNB4qwEbcqqMbyV5CyJd2sxQNUn6NEpMSsUb"
|
? "FvXhjZdGJT4JdaTJHcPtvogBsc1kbgiFo3utK6mZZzdP"
|
||||||
: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
|
: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
|
||||||
export const SOL_NFT_BRIDGE_ADDRESS =
|
export const SOL_NFT_BRIDGE_ADDRESS =
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "WnFt12ZrnzZrFZkt2xsNsaNWoQribnuQ5B5FrDbwDhD"
|
? "WnFt12ZrnzZrFZkt2xsNsaNWoQribnuQ5B5FrDbwDhD"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA" // TODO: test address
|
? "pnfZ3u1LPAaupt8YoZkxJkWUDoCZxs4XJkGibDQz7fW0"
|
||||||
: "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA";
|
: "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA";
|
||||||
export const SOL_TOKEN_BRIDGE_ADDRESS =
|
export const SOL_TOKEN_BRIDGE_ADDRESS =
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"
|
? "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "A4Us8EhCC76XdGAN17L4KpRNEK423nMivVHZzZqFqqBg"
|
? "GQemgcTaC6jojXS4pH4YPDD72b6RPsDhNPSjmxMfYcet"
|
||||||
: "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
|
: "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
|
||||||
|
|
||||||
export const SOL_CUSTODY_ADDRESS =
|
export const SOL_CUSTODY_ADDRESS =
|
||||||
|
@ -272,13 +270,13 @@ export const TERRA_BRIDGE_ADDRESS =
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5"
|
? "terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5"
|
? "terra1pd65m0q9tl3v8znnz5f5ltsfegyzah7g42cx5v"
|
||||||
: "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5";
|
: "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5";
|
||||||
export const TERRA_TOKEN_BRIDGE_ADDRESS =
|
export const TERRA_TOKEN_BRIDGE_ADDRESS =
|
||||||
CLUSTER === "mainnet"
|
CLUSTER === "mainnet"
|
||||||
? "terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf"
|
? "terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf"
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4"
|
? "terra1pseddrv0yfsn76u4zxrjmtf45kdlmalswdv39a"
|
||||||
: "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4";
|
: "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4";
|
||||||
|
|
||||||
export const getBridgeAddressForChain = (chainId: ChainId) =>
|
export const getBridgeAddressForChain = (chainId: ChainId) =>
|
||||||
|
@ -666,3 +664,5 @@ export const AVAILABLE_MARKETS_URL =
|
||||||
"https://docs.wormholenetwork.com/wormhole/overview-liquid-markets";
|
"https://docs.wormholenetwork.com/wormhole/overview-liquid-markets";
|
||||||
|
|
||||||
export const SOLANA_SYSTEM_PROGRAM_ADDRESS = "11111111111111111111111111111111";
|
export const SOLANA_SYSTEM_PROGRAM_ADDRESS = "11111111111111111111111111111111";
|
||||||
|
export const FEATURED_MARKETS_JSON_URL =
|
||||||
|
"https://raw.githubusercontent.com/certusone/wormhole-token-list/main/src/markets.json";
|
||||||
|
|
Loading…
Reference in New Issue