diff --git a/bridge_ui/src/components/TokenSelectors/SolanaSourceTokenSelector.tsx b/bridge_ui/src/components/TokenSelectors/SolanaSourceTokenSelector.tsx
index 57ba40d1b..662e40ffe 100644
--- a/bridge_ui/src/components/TokenSelectors/SolanaSourceTokenSelector.tsx
+++ b/bridge_ui/src/components/TokenSelectors/SolanaSourceTokenSelector.tsx
@@ -1,6 +1,6 @@
import { CircularProgress, TextField, Typography } from "@material-ui/core";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
-import { Autocomplete } from "@material-ui/lab";
+import { Alert, Autocomplete } from "@material-ui/lab";
import { createFilterOptions } from "@material-ui/lab/Autocomplete";
import { TokenInfo } from "@solana/spl-token-registry";
import React, { useCallback, useMemo } from "react";
@@ -8,7 +8,10 @@ import useMetaplexData from "../../hooks/useMetaplexData";
import useSolanaTokenMap from "../../hooks/useSolanaTokenMap";
import { DataWrapper } from "../../store/helpers";
import { ParsedTokenAccount } from "../../store/transferSlice";
-import { WORMHOLE_V1_MINT_AUTHORITY } from "../../utils/consts";
+import {
+ MIGRATION_ASSET_MAP,
+ WORMHOLE_V1_MINT_AUTHORITY,
+} from "../../utils/consts";
import { shortenAddress } from "../../utils/solana";
import NFTViewer from "./NFTViewer";
import RefreshButtonWrapper from "./RefreshButtonWrapper";
@@ -146,6 +149,10 @@ export default function SolanaSourceTokenSelector(
[props.mintAccounts]
);
+ const isMigrationEligible = useCallback((address: string) => {
+ return !!MIGRATION_ASSET_MAP.get(address);
+ }, []);
+
const renderAccount = useCallback(
(account: ParsedTokenAccount) => {
const mintPrettyString = shortenAddress(account.mintKey);
@@ -189,9 +196,24 @@ export default function SolanaSourceTokenSelector(
);
- return isWormholev1(account.mintKey) ? v1Warning : content;
+ const migrationRender = (
+
+
+
+ This is a legacy asset eligible for migration.
+
+ {content}
+
+
+ );
+
+ return isMigrationEligible(account.mintKey)
+ ? migrationRender
+ : isWormholev1(account.mintKey)
+ ? v1Warning
+ : content;
},
- [getLogo, getSymbol, getName, classes, isWormholev1]
+ [getLogo, getSymbol, getName, classes, isWormholev1, isMigrationEligible]
);
//The autocomplete doesn't rerender the option label unless the value changes.
@@ -218,8 +240,10 @@ export default function SolanaSourceTokenSelector(
}, [metaplex.data, nft, props.accounts]);
const isOptionDisabled = useMemo(() => {
- return (value: ParsedTokenAccount) => isWormholev1(value.mintKey);
- }, [isWormholev1]);
+ return (value: ParsedTokenAccount) => {
+ return isWormholev1(value.mintKey) && !isMigrationEligible(value.mintKey);
+ };
+ }, [isWormholev1, isMigrationEligible]);
const onAutocompleteChange = useCallback(
(event, newValue) => {
diff --git a/bridge_ui/src/components/Transfer/Source.tsx b/bridge_ui/src/components/Transfer/Source.tsx
index 3d25f8540..f847464d9 100644
--- a/bridge_ui/src/components/Transfer/Source.tsx
+++ b/bridge_ui/src/components/Transfer/Source.tsx
@@ -1,7 +1,9 @@
+import { CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
import { Restore } from "@material-ui/icons";
import { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
+import { useHistory } from "react-router";
import useIsWalletReady from "../../hooks/useIsWalletReady";
import useTokenBlacklistWarning from "../../hooks/useTokenBlacklistWarning";
import {
@@ -18,7 +20,7 @@ import {
setAmount,
setSourceChain,
} from "../../store/transferSlice";
-import { CHAINS } from "../../utils/consts";
+import { CHAINS, MIGRATION_ASSET_MAP } from "../../utils/consts";
import ButtonWithLoader from "../ButtonWithLoader";
import KeyAndBalance from "../KeyAndBalance";
import StepDescription from "../StepDescription";
@@ -37,11 +39,16 @@ function Source({
}) {
const classes = useStyles();
const dispatch = useDispatch();
+ const history = useHistory();
const sourceChain = useSelector(selectTransferSourceChain);
const parsedTokenAccount = useSelector(
selectTransferSourceParsedTokenAccount
);
const hasParsedTokenAccount = !!parsedTokenAccount;
+ const isMigrationAsset =
+ sourceChain === CHAIN_ID_SOLANA &&
+ !!parsedTokenAccount &&
+ !!MIGRATION_ASSET_MAP.get(parsedTokenAccount.mintKey);
const uiAmountString = useSelector(selectTransferSourceBalanceString);
const amount = useSelector(selectTransferAmount);
const error = useSelector(selectTransferSourceError);
@@ -52,6 +59,10 @@ function Source({
sourceChain,
parsedTokenAccount?.mintKey
);
+ const handleMigrationClick = useCallback(() => {
+ parsedTokenAccount?.mintKey &&
+ history.push("/migrate/" + parsedTokenAccount.mintKey);
+ }, [history, parsedTokenAccount]);
const handleSourceChange = useCallback(
(event) => {
dispatch(setSourceChain(event.target.value));
@@ -102,25 +113,38 @@ function Source({
) : null}
- {hasParsedTokenAccount ? (
-
- ) : null}
-
- Next
-
+ onClick={handleMigrationClick}
+ >
+ Go to Migration Page
+
+ ) : (
+ <>
+ {hasParsedTokenAccount ? (
+
+ ) : null}
+
+ Next
+
+ >
+ )}
>
);
}
diff --git a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.ts b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.ts
index e76e60128..b000ef021 100644
--- a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.ts
+++ b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.ts
@@ -371,7 +371,9 @@ function useGetAvailableTokens(nft: boolean = false) {
setSolanaMintAccountsError(undefined);
const mintAddresses = tokenAccounts.data.map((x) => x.mintKey);
//This is a known wormhole v1 token on testnet
- //mintAddresses.push("4QixXecTZ4zdZGa39KH8gVND5NZ2xcaB12wiBhE4S7rn");
+ // mintAddresses.push("4QixXecTZ4zdZGa39KH8gVND5NZ2xcaB12wiBhE4S7rn");
+ //SOLT devnet token
+ // mintAddresses.push("2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ");
const connection = new Connection(SOLANA_HOST, "finalized");
getMultipleAccountsRPC(