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) {
|
||||
//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(
|
||||
|
|
|
@ -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} >{" "}
|
||||
{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>
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -125,7 +125,7 @@ function Target() {
|
|||
fullWidth
|
||||
value={targetChain}
|
||||
onChange={handleTargetChange}
|
||||
disabled={shouldLockFields}
|
||||
disabled={true}
|
||||
chains={chains}
|
||||
/>
|
||||
<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) => {
|
||||
return state.tokens.terraTokenMap;
|
||||
};
|
||||
|
||||
export const selectMarketsMap = (state: RootState) => {
|
||||
return state.tokens.marketsMap;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
Loading…
Reference in New Issue