bridge_ui: featured markets

Change-Id: I72a48fd3f1ff674d64aad58c9a0020585d234c82
This commit is contained in:
Evan Gray 2021-11-20 00:43:47 -05:00
parent 54ebc6481f
commit 793a4b4f5f
8 changed files with 372 additions and 74 deletions

View File

@ -146,36 +146,16 @@ export default function EvmTokenPicker(
} catch (e) {
//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);
if (v1 === true && !migration) {
throw new Error(
"Wormhole v1 assets cannot be transferred with this bridge."
);
}
onChange(newAccount);
onChange(account);
return Promise.resolve();
},
[chainId, onChange, provider, isMigrationEligible, getAddress]
[chainId, onChange, provider, isMigrationEligible]
);
const RenderComp = useCallback(

View File

@ -7,7 +7,9 @@ import {
Dialog,
DialogContent,
DialogTitle,
Divider,
IconButton,
Link,
List,
ListItem,
makeStyles,
@ -15,11 +17,16 @@ import {
Tooltip,
Typography,
} from "@material-ui/core";
import { InfoOutlined, Launch } from "@material-ui/icons";
import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown";
import RefreshIcon from "@material-ui/icons/Refresh";
import { Alert } from "@material-ui/lab";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useSelector } from "react-redux";
import useMarketsMap from "../../hooks/useMarketsMap";
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 NFTViewer from "./NFTViewer";
@ -62,6 +69,11 @@ const useStyles = makeStyles((theme) =>
"&$tokenImageContainer": {
maxWidth: 40,
},
"&$tokenMarketsList": {
marginTop: theme.spacing(-0.5),
marginLeft: 0,
flexBasis: "100%",
},
"&:last-child": {
textAlign: "right",
},
@ -78,6 +90,15 @@ const useStyles = makeStyles((theme) =>
tokenImage: {
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: {
width: "100%",
"& .MuiAlert-message": {
@ -107,12 +128,17 @@ export const balancePretty = (uiString: string) => {
}
};
const noClickThrough = (e: any) => {
e.stopPropagation();
};
export const BasicAccountRender = (
account: NFTParsedTokenAccount,
account: MarketParsedTokenAccount,
isMigrationEligible: (address: string) => boolean,
nft: boolean,
displayBalance?: (account: NFTParsedTokenAccount) => boolean
) => {
const { data: marketsData } = useMarketsMap(false);
const classes = useStyles();
const mintPrettyString = shortenAddress(account.mintKey);
const uri = nft ? account.image_256 : account.logo || account.uri;
@ -139,6 +165,27 @@ export const BasicAccountRender = (
const tokenContent = (
<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}>
{uri && <img alt="" className={classes.tokenImage} src={uri} />}
</div>
@ -185,6 +232,10 @@ export const BasicAccountRender = (
: tokenContent;
};
interface MarketParsedTokenAccount extends NFTParsedTokenAccount {
markets?: string[];
}
export default function TokenPicker({
value,
options,
@ -229,6 +280,9 @@ export default function TokenPicker({
const [dialogIsOpen, setDialogIsOpen] = useState(false);
const [selectionError, setSelectionError] = useState("");
const targetChain = useSelector(selectTransferTargetChain);
const { data: marketsData } = useMarketsMap(true);
const openDialog = useCallback(() => {
setHolderString("");
setSelectionError("");
@ -242,16 +296,31 @@ export default function TokenPicker({
const handleSelectOption = useCallback(
async (option: NFTParsedTokenAccount) => {
setSelectionError("");
onChange(option).then(
() => {
closeDialog();
},
(error) => {
setSelectionError(error?.message || "Error verifying the token.");
let newOption = null;
try {
//Covalent balances tend to be stale, so we make an attempt to correct it at selection time.
if (getAddress && !option.isNativeAsset) {
newOption = await getAddress(option.mintKey, option.tokenId);
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(() => {
@ -261,8 +330,8 @@ export default function TokenPicker({
resetAccounts && resetAccounts();
}, [resetAccounts]);
const filteredOptions = useMemo(() => {
return options.filter((option: NFTParsedTokenAccount) => {
const searchFilter = useCallback(
(option: NFTParsedTokenAccount) => {
if (!holderString) {
return true;
}
@ -277,8 +346,61 @@ export default function TokenPicker({
).toLowerCase();
const searchString = holderString.toLowerCase();
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(
(address: string, tokenIdHolderString: string) => {
@ -383,6 +505,17 @@ export default function TokenPicker({
</div>
</DialogTitle>
<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
variant="outlined"
label="Search name or paste address"
@ -405,11 +538,52 @@ export default function TokenPicker({
localLoader
) : loadingError || selectionError ? (
displayLocalError
) : filteredOptions.length ? (
<List className={classes.tokenList}>
{filteredOptions.map((option) => {
) : (
<List component="div" className={classes.tokenList}>
{featuredOptions.length ? (
<>
<Typography variant="subtitle2" gutterBottom>
Featured {CHAINS_BY_ID[chainId].name} &gt;{" "}
{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 (
<ListItem
component="div"
button
onClick={() => handleSelectOption(option)}
key={
@ -420,11 +594,12 @@ export default function TokenPicker({
</ListItem>
);
})}
{featuredOptions.length || nonFeaturedOptions.length ? null : (
<div className={classes.alignCenter}>
<Typography>No results found</Typography>
</div>
)}
</List>
) : (
<div className={classes.alignCenter}>
<Typography>No results found</Typography>
</div>
)}
</DialogContent>
</Dialog>

View File

@ -4,12 +4,12 @@ import {
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import { getAddress } from "@ethersproject/address";
import { Button, makeStyles } from "@material-ui/core";
import { Link } from "react-router-dom";
import { VerifiedUser } from "@material-ui/icons";
import { useCallback } from "react";
import { Button, makeStyles, Typography } from "@material-ui/core";
import { ArrowForward, VerifiedUser } from "@material-ui/icons";
import { useCallback, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useHistory } from "react-router";
import { Link } from "react-router-dom";
import useIsWalletReady from "../../hooks/useIsWalletReady";
import {
selectTransferAmount,
@ -19,11 +19,13 @@ import {
selectTransferSourceChain,
selectTransferSourceError,
selectTransferSourceParsedTokenAccount,
selectTransferTargetChain,
} from "../../store/selectors";
import {
incrementStep,
setAmount,
setSourceChain,
setTargetChain,
} from "../../store/transferSlice";
import {
BSC_MIGRATION_ASSET_MAP,
@ -40,6 +42,24 @@ import StepDescription from "../StepDescription";
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
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: {
marginTop: theme.spacing(5),
},
@ -50,6 +70,11 @@ function Source() {
const dispatch = useDispatch();
const history = useHistory();
const sourceChain = useSelector(selectTransferSourceChain);
const targetChain = useSelector(selectTransferTargetChain);
const targetChainOptions = useMemo(
() => CHAINS.filter((c) => c.id !== sourceChain),
[sourceChain]
);
const parsedTokenAccount = useSelector(
selectTransferSourceParsedTokenAccount
);
@ -91,6 +116,12 @@ function Source() {
},
[dispatch]
);
const handleTargetChange = useCallback(
(event) => {
dispatch(setTargetChain(event.target.value));
},
[dispatch]
);
const handleAmountChange = useCallback(
(event) => {
dispatch(setAmount(event.target.value));
@ -124,15 +155,35 @@ function Source() {
</div>
</div>
</StepDescription>
<ChainSelect
select
variant="outlined"
fullWidth
value={sourceChain}
onChange={handleSourceChange}
disabled={shouldLockFields}
chains={CHAINS}
/>
<div className={classes.chainSelectWrapper}>
<div className={classes.chainSelectContainer}>
<Typography variant="caption">Source</Typography>
<ChainSelect
select
variant="outlined"
fullWidth
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} />
{isReady || uiAmountString ? (
<div className={classes.transferField}>

View File

@ -125,7 +125,7 @@ function Target() {
fullWidth
value={targetChain}
onChange={handleTargetChange}
disabled={shouldLockFields}
disabled={true}
chains={chains}
/>
<KeyAndBalance chainId={targetChain} />

View File

@ -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;

View File

@ -292,3 +292,7 @@ export const selectSolanaTokenMap = (state: RootState) => {
export const selectTerraTokenMap = (state: RootState) => {
return state.tokens.terraTokenMap;
};
export const selectMarketsMap = (state: RootState) => {
return state.tokens.marketsMap;
};

View File

@ -1,6 +1,7 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { TokenInfo } from "@solana/spl-token-registry";
import { TerraTokenMap } from "../hooks/useTerraTokenMap";
import { MarketsMap } from "../hooks/useMarketsMap";
import {
DataWrapper,
errorDataWrapper,
@ -12,11 +13,13 @@ import {
export interface TokenMetadataState {
solanaTokenMap: DataWrapper<TokenInfo[]>;
terraTokenMap: DataWrapper<TerraTokenMap>; //TODO make a decent type for this.
marketsMap: DataWrapper<MarketsMap>;
}
const initialState: TokenMetadataState = {
solanaTokenMap: getEmptyDataWrapper(),
terraTokenMap: getEmptyDataWrapper(),
marketsMap: getEmptyDataWrapper(),
};
export const tokenSlice = createSlice({
@ -43,6 +46,16 @@ export const tokenSlice = createSlice({
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,
},
});
@ -54,6 +67,9 @@ export const {
receiveTerraTokenMap,
fetchTerraTokenMap,
errorTerraTokenMap,
receiveMarketsMap,
fetchMarketsMap,
errorMarketsMap,
reset,
} = tokenSlice.actions;

View File

@ -136,9 +136,7 @@ export const WORMHOLE_RPC_HOSTS =
"https://wormhole-v2-mainnet-api.chainlayer.network",
]
: CLUSTER === "testnet"
? [
"https://wormhole-v2-testnet-api.certus.one",
]
? ["https://wormhole-v2-testnet-api.certus.one"]
: ["http://localhost:7071"];
export const ETH_NETWORK_CHAIN_ID =
CLUSTER === "mainnet" ? 1 : CLUSTER === "testnet" ? 5 : 1337;
@ -159,7 +157,7 @@ export const SOLANA_HOST = process.env.REACT_APP_SOLANA_API_URL
: CLUSTER === "mainnet"
? clusterApiUrl("mainnet-beta")
: CLUSTER === "testnet"
? clusterApiUrl("testnet")
? clusterApiUrl("devnet")
: "http://localhost:8899";
export const TERRA_HOST =
@ -184,82 +182,82 @@ export const ETH_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
: CLUSTER === "testnet"
? "0x44F3e7c20850B3B5f3031114726A9240911D912a"
? "0xC0231E0957596A90004119f4254aff364f6f1002"
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const ETH_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE"
: CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
? "0x5B78d166Fc3C2c99783B60b959dC35E316EBB5e7"
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const ETH_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
: CLUSTER === "testnet"
? "0xa6CDAddA6e4B6704705b065E01E52e2486c0FBf6"
? "0xc59072C84ECD13DbF30856021C0a33868121Cb9d"
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const BSC_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
: CLUSTER === "testnet"
? "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" // TODO: test address
? "0x61D9309dC73CcAC3c639aeC497A11320C5A72074"
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const BSC_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
: CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
? "0x55A525D72f4b08762991e4ECDB1aDb5Ab55dFf37"
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const BSC_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7"
: CLUSTER === "testnet"
? "0x0290FB167208Af455bB137780163b7B7a9a10C16" // TODO: test address
? "0xC708B76f0C28040A0f852DbacB26375eDB071c1D"
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const POLYGON_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7"
: CLUSTER === "testnet"
? "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" // TODO: test address
? "0x61D9309dC73CcAC3c639aeC497A11320C5A72074"
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const POLYGON_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x90BBd86a6Fe93D3bc3ed6335935447E75fAb7fCf"
: CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
? "0x55A525D72f4b08762991e4ECDB1aDb5Ab55dFf37"
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const POLYGON_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
: CLUSTER === "testnet"
? "0x0290FB167208Af455bB137780163b7B7a9a10C16" // TODO: test address
? "0xC708B76f0C28040A0f852DbacB26375eDB071c1D"
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const SOL_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"
: CLUSTER === "testnet"
? "Brdguy7BmNB4qwEbcqqMbyV5CyJd2sxQNUn6NEpMSsUb"
? "FvXhjZdGJT4JdaTJHcPtvogBsc1kbgiFo3utK6mZZzdP"
: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
export const SOL_NFT_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "WnFt12ZrnzZrFZkt2xsNsaNWoQribnuQ5B5FrDbwDhD"
: CLUSTER === "testnet"
? "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA" // TODO: test address
? "pnfZ3u1LPAaupt8YoZkxJkWUDoCZxs4XJkGibDQz7fW0"
: "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA";
export const SOL_TOKEN_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"
: CLUSTER === "testnet"
? "A4Us8EhCC76XdGAN17L4KpRNEK423nMivVHZzZqFqqBg"
? "GQemgcTaC6jojXS4pH4YPDD72b6RPsDhNPSjmxMfYcet"
: "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
export const SOL_CUSTODY_ADDRESS =
@ -272,13 +270,13 @@ export const TERRA_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5"
: CLUSTER === "testnet"
? "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5"
? "terra1pd65m0q9tl3v8znnz5f5ltsfegyzah7g42cx5v"
: "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5";
export const TERRA_TOKEN_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf"
: CLUSTER === "testnet"
? "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4"
? "terra1pseddrv0yfsn76u4zxrjmtf45kdlmalswdv39a"
: "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4";
export const getBridgeAddressForChain = (chainId: ChainId) =>
@ -666,3 +664,5 @@ export const AVAILABLE_MARKETS_URL =
"https://docs.wormholenetwork.com/wormhole/overview-liquid-markets";
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";