bridge_ui: updated warnings & confirm dialog
Change-Id: I6b5e471ddf2d4188932b72647f92fcf4adfa7555
This commit is contained in:
parent
c824a99636
commit
4b510d8aa1
|
@ -1,3 +1,4 @@
|
||||||
|
import { isEVMChain } from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
|
@ -7,62 +8,142 @@ import {
|
||||||
Typography,
|
Typography,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import { ArrowDownward } from "@material-ui/icons";
|
import { ArrowDownward } from "@material-ui/icons";
|
||||||
import { Alert } from "@material-ui/lab";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
|
selectTransferOriginChain,
|
||||||
selectTransferSourceChain,
|
selectTransferSourceChain,
|
||||||
selectTransferSourceParsedTokenAccount,
|
selectTransferSourceParsedTokenAccount,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { CHAINS_BY_ID } from "../../utils/consts";
|
import { CHAINS_BY_ID, MULTI_CHAIN_TOKENS } from "../../utils/consts";
|
||||||
import SmartAddress from "../SmartAddress";
|
import SmartAddress from "../SmartAddress";
|
||||||
import { useTargetInfo } from "./Target";
|
import { useTargetInfo } from "./Target";
|
||||||
|
import TokenWarning from "./TokenWarning";
|
||||||
|
|
||||||
function SendConfirmationContent() {
|
function SendConfirmationContent({
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
onClick,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onClick: () => void;
|
||||||
|
}) {
|
||||||
const sourceChain = useSelector(selectTransferSourceChain);
|
const sourceChain = useSelector(selectTransferSourceChain);
|
||||||
const sourceParsedTokenAccount = useSelector(
|
const sourceParsedTokenAccount = useSelector(
|
||||||
selectTransferSourceParsedTokenAccount
|
selectTransferSourceParsedTokenAccount
|
||||||
);
|
);
|
||||||
const { targetChain, targetAsset, symbol, tokenName, logo } = useTargetInfo();
|
const { targetChain, targetAsset, symbol, tokenName, logo } = useTargetInfo();
|
||||||
return (
|
const originChain = useSelector(selectTransferOriginChain);
|
||||||
|
|
||||||
|
//TODO this check is essentially duplicated.
|
||||||
|
const deservesTimeout = useMemo(() => {
|
||||||
|
if (originChain && sourceParsedTokenAccount?.mintKey) {
|
||||||
|
const searchableAddress = isEVMChain(originChain)
|
||||||
|
? sourceParsedTokenAccount.mintKey.toLowerCase()
|
||||||
|
: sourceParsedTokenAccount.mintKey;
|
||||||
|
return (
|
||||||
|
originChain !== targetChain &&
|
||||||
|
!!MULTI_CHAIN_TOKENS[sourceChain]?.[searchableAddress]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, [originChain, targetChain, sourceChain, sourceParsedTokenAccount]);
|
||||||
|
const timeoutDuration = 5;
|
||||||
|
|
||||||
|
const [countdown, setCountdown] = useState(
|
||||||
|
deservesTimeout ? timeoutDuration : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!deservesTimeout || countdown === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if (!cancelled) {
|
||||||
|
setCountdown((state) => state - 1);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [deservesTimeout, countdown]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && deservesTimeout) {
|
||||||
|
//Countdown starts on mount, but we actually want it to start on open
|
||||||
|
setCountdown(timeoutDuration);
|
||||||
|
}
|
||||||
|
}, [open, deservesTimeout]);
|
||||||
|
|
||||||
|
const sendConfirmationContent = (
|
||||||
<>
|
<>
|
||||||
{targetAsset ? (
|
<DialogTitle>Are you sure?</DialogTitle>
|
||||||
<div style={{ textAlign: "center" }}>
|
<DialogContent>
|
||||||
<SmartAddress
|
{targetAsset ? (
|
||||||
variant="h6"
|
<div style={{ textAlign: "center", marginBottom: 16 }}>
|
||||||
chainId={sourceChain}
|
<Typography variant="subtitle1" style={{ marginBottom: 8 }}>
|
||||||
parsedTokenAccount={sourceParsedTokenAccount}
|
You are about to perform this transfer:
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<Typography variant="caption">
|
|
||||||
{CHAINS_BY_ID[sourceChain].name}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<SmartAddress
|
||||||
|
variant="h6"
|
||||||
|
chainId={sourceChain}
|
||||||
|
parsedTokenAccount={sourceParsedTokenAccount}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<Typography variant="caption">
|
||||||
|
{CHAINS_BY_ID[sourceChain].name}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div style={{ paddingTop: 4 }}>
|
||||||
|
<ArrowDownward fontSize="inherit" />
|
||||||
|
</div>
|
||||||
|
<SmartAddress
|
||||||
|
variant="h6"
|
||||||
|
chainId={targetChain}
|
||||||
|
address={targetAsset}
|
||||||
|
symbol={symbol}
|
||||||
|
tokenName={tokenName}
|
||||||
|
logo={logo}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<Typography variant="caption">
|
||||||
|
{CHAINS_BY_ID[targetChain].name}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ paddingTop: 4 }}>
|
) : null}
|
||||||
<ArrowDownward fontSize="inherit" />
|
<TokenWarning
|
||||||
</div>
|
sourceAsset={sourceParsedTokenAccount?.mintKey}
|
||||||
<SmartAddress
|
sourceChain={sourceChain}
|
||||||
variant="h6"
|
originChain={originChain}
|
||||||
chainId={targetChain}
|
targetAsset={targetAsset ?? undefined}
|
||||||
address={targetAsset}
|
targetChain={targetChain}
|
||||||
symbol={symbol}
|
symbol={symbol}
|
||||||
tokenName={tokenName}
|
/>
|
||||||
logo={logo}
|
</DialogContent>
|
||||||
/>
|
<DialogActions>
|
||||||
<div>
|
<Button variant="outlined" onClick={onClose}>
|
||||||
<Typography variant="caption">
|
Cancel
|
||||||
{CHAINS_BY_ID[targetChain].name}
|
</Button>
|
||||||
</Typography>
|
<Button
|
||||||
</div>
|
variant="contained"
|
||||||
</div>
|
color="primary"
|
||||||
) : null}
|
onClick={onClick}
|
||||||
<Alert severity="warning" variant="outlined" style={{ marginTop: 8 }}>
|
size={"medium"}
|
||||||
Once the transfer transaction is submitted, the transfer must be
|
disabled={!!countdown}
|
||||||
completed by redeeming the tokens on the target chain. Please ensure
|
>
|
||||||
that the token listed above is the desired token and confirm that
|
{!!countdown ? countdown.toString() : "Confirm"}
|
||||||
markets exist on the target chain.
|
</Button>
|
||||||
</Alert>
|
</DialogActions>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return sendConfirmationContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SendConfirmationDialog({
|
export default function SendConfirmationDialog({
|
||||||
|
@ -76,18 +157,11 @@ export default function SendConfirmationDialog({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onClose={onClose}>
|
<Dialog open={open} onClose={onClose}>
|
||||||
<DialogTitle>Are you sure?</DialogTitle>
|
<SendConfirmationContent
|
||||||
<DialogContent>
|
open={open}
|
||||||
<SendConfirmationContent />
|
onClose={onClose}
|
||||||
</DialogContent>
|
onClick={onClick}
|
||||||
<DialogActions>
|
/>
|
||||||
<Button variant="outlined" onClick={onClose}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button variant="contained" color="primary" onClick={onClick}>
|
|
||||||
Confirm
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ import LowBalanceWarning from "../LowBalanceWarning";
|
||||||
import NumberTextField from "../NumberTextField";
|
import NumberTextField from "../NumberTextField";
|
||||||
import StepDescription from "../StepDescription";
|
import StepDescription from "../StepDescription";
|
||||||
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
import { TokenSelector } from "../TokenSelectors/SourceTokenSelector";
|
||||||
import TokenWarning from "./TokenWarning";
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
transferField: {
|
transferField: {
|
||||||
|
@ -135,11 +134,6 @@ function Source() {
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<TokenWarning
|
|
||||||
sourceChain={sourceChain}
|
|
||||||
tokenAddress={parsedTokenAccount?.mintKey}
|
|
||||||
symbol={parsedTokenAccount?.symbol}
|
|
||||||
/>
|
|
||||||
<LowBalanceWarning chainId={sourceChain} />
|
<LowBalanceWarning chainId={sourceChain} />
|
||||||
{hasParsedTokenAccount ? (
|
{hasParsedTokenAccount ? (
|
||||||
<NumberTextField
|
<NumberTextField
|
||||||
|
|
|
@ -8,7 +8,6 @@ import {
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { CHAINS_BY_ID } from "../../utils/consts";
|
import { CHAINS_BY_ID } from "../../utils/consts";
|
||||||
import SmartAddress from "../SmartAddress";
|
import SmartAddress from "../SmartAddress";
|
||||||
import TokenWarning from "./TokenWarning";
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
description: {
|
description: {
|
||||||
|
@ -54,11 +53,6 @@ export default function SourcePreview() {
|
||||||
>
|
>
|
||||||
{explainerContent}
|
{explainerContent}
|
||||||
</Typography>
|
</Typography>
|
||||||
<TokenWarning
|
|
||||||
sourceChain={sourceChain}
|
|
||||||
tokenAddress={sourceParsedTokenAccount?.mintKey}
|
|
||||||
symbol={sourceParsedTokenAccount?.symbol}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,10 @@
|
||||||
import {
|
import { ChainId, CHAIN_ID_ETH, isEVMChain } from "@certusone/wormhole-sdk";
|
||||||
ChainId,
|
import { Box, Link, makeStyles, Typography } from "@material-ui/core";
|
||||||
CHAIN_ID_BSC,
|
|
||||||
CHAIN_ID_ETH,
|
|
||||||
CHAIN_ID_SOLANA,
|
|
||||||
WSOL_ADDRESS,
|
|
||||||
} from "@certusone/wormhole-sdk";
|
|
||||||
import { getAddress } from "@ethersproject/address";
|
|
||||||
import { makeStyles } from "@material-ui/core";
|
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
import { useMemo } from "react";
|
|
||||||
import {
|
import {
|
||||||
BSC_MARKET_WARNINGS,
|
AVAILABLE_MARKETS_URL,
|
||||||
ETH_TOKENS_THAT_CAN_BE_SWAPPED_ON_SOLANA,
|
CHAINS_BY_ID,
|
||||||
ETH_TOKENS_THAT_EXIST_ELSEWHERE,
|
MULTI_CHAIN_TOKENS,
|
||||||
SOLANA_TOKENS_THAT_EXIST_ELSEWHERE,
|
|
||||||
} from "../../utils/consts";
|
} from "../../utils/consts";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
@ -21,81 +12,122 @@ const useStyles = makeStyles((theme) => ({
|
||||||
marginTop: theme.spacing(2),
|
marginTop: theme.spacing(2),
|
||||||
marginBottom: theme.spacing(2),
|
marginBottom: theme.spacing(2),
|
||||||
},
|
},
|
||||||
|
alert: {
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
line: {
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function TokenWarning({
|
function WormholeWrappedWarning() {
|
||||||
sourceChain,
|
const classes = useStyles();
|
||||||
tokenAddress,
|
return (
|
||||||
|
<Alert severity="info" variant="outlined" className={classes.alert}>
|
||||||
|
<Typography component="div" className={classes.line}>
|
||||||
|
The tokens you will receive are{" "}
|
||||||
|
<Box fontWeight={900} display="inline">
|
||||||
|
Wormhole Wrapped Tokens
|
||||||
|
</Box>{" "}
|
||||||
|
and will need to be exchanged for native assets.
|
||||||
|
</Typography>
|
||||||
|
<Typography component="div">
|
||||||
|
<Link
|
||||||
|
href={AVAILABLE_MARKETS_URL}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Click here to see available markets for wrapped tokens.
|
||||||
|
</Link>
|
||||||
|
</Typography>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MultichainWarning({
|
||||||
symbol,
|
symbol,
|
||||||
|
targetChain,
|
||||||
}: {
|
}: {
|
||||||
sourceChain: ChainId;
|
symbol: string;
|
||||||
tokenAddress: string | undefined;
|
targetChain: ChainId;
|
||||||
symbol: string | undefined;
|
|
||||||
}) {
|
}) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const tokenConflictingNativeWarning = useMemo(
|
return (
|
||||||
() =>
|
<Alert severity="warning" variant="outlined" className={classes.alert}>
|
||||||
tokenAddress &&
|
<Typography
|
||||||
((sourceChain === CHAIN_ID_SOLANA &&
|
variant="h6"
|
||||||
SOLANA_TOKENS_THAT_EXIST_ELSEWHERE.includes(tokenAddress)) ||
|
className={classes.line}
|
||||||
(sourceChain === CHAIN_ID_ETH &&
|
>{`You will not receive native ${symbol} on ${CHAINS_BY_ID[targetChain].name}`}</Typography>
|
||||||
ETH_TOKENS_THAT_EXIST_ELSEWHERE.includes(getAddress(tokenAddress))))
|
<Typography
|
||||||
? `Bridging ${
|
className={classes.line}
|
||||||
symbol ? symbol : "the token"
|
>{`To receive native ${symbol}, you will have to perform a swap with the wrapped tokens once you are done bridging.`}</Typography>
|
||||||
} via Wormhole will not produce native ${
|
<Typography>
|
||||||
symbol ? symbol : "assets"
|
<Link
|
||||||
}. It will produce a wrapped version which might have no liquidity or utility on the target chain.`
|
href={AVAILABLE_MARKETS_URL}
|
||||||
: undefined,
|
target="_blank"
|
||||||
[sourceChain, tokenAddress, symbol]
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Click here to see available markets for wrapped tokens.
|
||||||
|
</Link>
|
||||||
|
</Typography>
|
||||||
|
</Alert>
|
||||||
);
|
);
|
||||||
const marketsWarning = useMemo(() => {
|
}
|
||||||
let show = false;
|
|
||||||
if (sourceChain === CHAIN_ID_SOLANA && tokenAddress === WSOL_ADDRESS) {
|
|
||||||
show = true;
|
|
||||||
} else if (
|
|
||||||
sourceChain === CHAIN_ID_BSC &&
|
|
||||||
tokenAddress &&
|
|
||||||
BSC_MARKET_WARNINGS.includes(getAddress(tokenAddress))
|
|
||||||
) {
|
|
||||||
show = true;
|
|
||||||
}
|
|
||||||
if (show) {
|
|
||||||
return `As of 10/13/2021, markets have not been established for ${
|
|
||||||
symbol ? "Wormhole-wrapped " + symbol : "this token"
|
|
||||||
}. Please verify this token will be useful on the target chain.`;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}, [sourceChain, tokenAddress, symbol]);
|
|
||||||
|
|
||||||
const content = tokenConflictingNativeWarning ? (
|
function RewardsWarning() {
|
||||||
<Alert severity="warning" variant="outlined">
|
const classes = useStyles();
|
||||||
{tokenConflictingNativeWarning}
|
return (
|
||||||
</Alert>
|
<Alert severity="warning" variant="outlined" className={classes.alert}>
|
||||||
) : marketsWarning ? (
|
|
||||||
<Alert severity="warning" variant="outlined">
|
|
||||||
{marketsWarning}
|
|
||||||
</Alert>
|
|
||||||
) : sourceChain === CHAIN_ID_ETH &&
|
|
||||||
tokenAddress &&
|
|
||||||
getAddress(tokenAddress) ===
|
|
||||||
getAddress("0xae7ab96520de3a18e5e111b5eaab095312d7fe84") ? ( // stETH (Lido)
|
|
||||||
<Alert severity="warning" variant="outlined">
|
|
||||||
Lido stETH rewards can only be received on Ethereum. Use the value
|
Lido stETH rewards can only be received on Ethereum. Use the value
|
||||||
accruing wrapper token wstETH instead.
|
accruing wrapper token wstETH instead.
|
||||||
</Alert>
|
</Alert>
|
||||||
) : sourceChain === CHAIN_ID_ETH &&
|
);
|
||||||
tokenAddress &&
|
}
|
||||||
ETH_TOKENS_THAT_CAN_BE_SWAPPED_ON_SOLANA.includes(
|
|
||||||
getAddress(tokenAddress)
|
export default function TokenWarning({
|
||||||
) ? (
|
sourceChain,
|
||||||
//TODO: will this be accurate with Terra support?
|
sourceAsset,
|
||||||
<Alert severity="info" variant="outlined">
|
originChain,
|
||||||
Bridging {symbol ? symbol : "the token"} via Wormhole will not produce
|
targetChain,
|
||||||
native {symbol ? symbol : "assets"}. It will produce a wrapped version
|
targetAsset,
|
||||||
which can be swapped using a stable swap protocol.
|
symbol,
|
||||||
</Alert>
|
}: {
|
||||||
) : null;
|
sourceChain?: ChainId;
|
||||||
|
sourceAsset?: string;
|
||||||
return content ? <div className={classes.container}>{content}</div> : null;
|
originChain?: ChainId;
|
||||||
|
targetChain?: ChainId;
|
||||||
|
targetAsset?: string;
|
||||||
|
symbol?: string;
|
||||||
|
}) {
|
||||||
|
if (
|
||||||
|
!(originChain && targetChain && targetAsset && sourceChain && sourceAsset)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchableAddress = isEVMChain(sourceChain)
|
||||||
|
? sourceAsset.toLowerCase()
|
||||||
|
: sourceAsset;
|
||||||
|
const isWormholeWrapped = originChain !== targetChain;
|
||||||
|
const isMultiChain = !!MULTI_CHAIN_TOKENS[sourceChain]?.[searchableAddress];
|
||||||
|
const isRewardsToken =
|
||||||
|
searchableAddress === "0xae7ab96520de3a18e5e111b5eaab095312d7fe84" &&
|
||||||
|
sourceChain === CHAIN_ID_ETH;
|
||||||
|
|
||||||
|
const showMultiChainWarning = isMultiChain && isWormholeWrapped;
|
||||||
|
const showWrappedWarning = !isMultiChain && isWormholeWrapped; //Multichain warning is more important
|
||||||
|
const showRewardsWarning = isRewardsToken;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{showMultiChainWarning ? (
|
||||||
|
<MultichainWarning
|
||||||
|
symbol={symbol || "tokens"}
|
||||||
|
targetChain={targetChain}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{showWrappedWarning ? <WormholeWrappedWarning /> : null}
|
||||||
|
{showRewardsWarning ? <RewardsWarning /> : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -629,3 +629,36 @@ export const VAA_EMITTER_ADDRESSES = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export const WORMHOLE_EXPLORER_BASE = "https://wormholenetwork.com/en/explorer";
|
export const WORMHOLE_EXPLORER_BASE = "https://wormholenetwork.com/en/explorer";
|
||||||
|
|
||||||
|
export const MULTI_CHAIN_TOKENS: {
|
||||||
|
[x: number]: { [address: string]: string };
|
||||||
|
} =
|
||||||
|
//EVM chains should format the addresses to all lowercase
|
||||||
|
CLUSTER === "mainnet"
|
||||||
|
? {
|
||||||
|
[CHAIN_ID_SOLANA]: {
|
||||||
|
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: "USDC",
|
||||||
|
Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB: "USDT",
|
||||||
|
},
|
||||||
|
[CHAIN_ID_ETH]: {
|
||||||
|
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "USDC",
|
||||||
|
"0xdac17f958d2ee523a2206206994597c13d831ec7": "USDT",
|
||||||
|
},
|
||||||
|
[CHAIN_ID_TERRA]: {},
|
||||||
|
[CHAIN_ID_BSC]: {
|
||||||
|
"0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d": "USDC",
|
||||||
|
"0x55d398326f99059ff775485246999027b3197955": "USDT",
|
||||||
|
},
|
||||||
|
[CHAIN_ID_POLYGON]: {
|
||||||
|
"0x2791bca1f2de4661ed88a30c99a7a9449aa84174": "USDC",
|
||||||
|
"0xc2132d05d31c914a87c6611c10748aeb04b58e8f": "USDT",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
[CHAIN_ID_SOLANA]: {
|
||||||
|
"2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ": "SOLT",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AVAILABLE_MARKETS_URL =
|
||||||
|
"https://docs.wormholenetwork.com/wormhole/overview-liquid-markets";
|
||||||
|
|
Loading…
Reference in New Issue