bridge_ui: add bsc migration support

Change-Id: Ia11a78be9318406ab72d7c9a58c6e74699379cff
This commit is contained in:
Chase Moran 2021-10-15 12:30:49 -04:00
parent e8b51439fe
commit 350244e8e4
7 changed files with 108 additions and 50 deletions

View File

@ -1,4 +1,8 @@
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import {
CHAIN_ID_BSC,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import {
AppBar,
Button,
@ -28,7 +32,7 @@ import Attest from "./components/Attest";
import Footer from "./components/Footer";
import Home from "./components/Home";
import Migration from "./components/Migration";
import EthereumQuickMigrate from "./components/Migration/EthereumQuickMigrate";
import EvmQuickMigrate from "./components/Migration/EvmQuickMigrate";
import NFT from "./components/NFT";
import NFTOriginVerifier from "./components/NFTOriginVerifier";
import Recovery from "./components/Recovery";
@ -124,6 +128,7 @@ function App() {
const isBeta = useBetaContext();
const isHomepage = useRouteMatch({ path: "/", exact: true });
const isStats = useRouteMatch({ path: "/stats", exact: true });
const isMigrate = useRouteMatch({ path: "/migrate" });
const isOriginVerifier = useRouteMatch({
path: "/nft-origin-verifier",
exact: true,
@ -244,7 +249,7 @@ function App() {
</AppBar>
) : null}
<div className={classes.content}>
{isHomepage || isOriginVerifier || isStats ? null : (
{isHomepage || isOriginVerifier || isStats || isMigrate ? null : (
<Container maxWidth="md" style={{ paddingBottom: 24 }}>
<Tabs
value={
@ -284,8 +289,14 @@ function App() {
<Route exact path="/migrate/Ethereum/:legacyAsset/">
<Migration chainId={CHAIN_ID_ETH} />
</Route>
<Route exact path="/migrate/BinanceSmartChain/:legacyAsset/">
<Migration chainId={CHAIN_ID_BSC} />
</Route>
<Route exact path="/migrate/Ethereum/">
<EthereumQuickMigrate />
<EvmQuickMigrate chainId={CHAIN_ID_ETH} />
</Route>
<Route exact path="/migrate/BinanceSmartChain/">
<EvmQuickMigrate chainId={CHAIN_ID_BSC} />
</Route>
<Route exact path="/stats">
<Stats />

View File

@ -1,7 +1,4 @@
import {
CHAIN_ID_ETH,
TokenImplementation__factory,
} from "@certusone/wormhole-sdk";
import { ChainId, TokenImplementation__factory } from "@certusone/wormhole-sdk";
import { Signer } from "@ethersproject/abstract-signer";
import { BigNumber } from "@ethersproject/bignumber";
import {
@ -20,7 +17,7 @@ import { useEthereumProvider } from "../../contexts/EthereumProviderContext";
import useEthereumMigratorInformation from "../../hooks/useEthereumMigratorInformation";
import useIsWalletReady from "../../hooks/useIsWalletReady";
import { COLORS } from "../../muiTheme";
import { ETH_MIGRATION_ASSET_MAP } from "../../utils/consts";
import { CHAINS, getMigrationAssetMap } from "../../utils/consts";
import ButtonWithLoader from "../ButtonWithLoader";
import EthereumSignerKey from "../EthereumSignerKey";
import ShowTx from "../ShowTx";
@ -89,10 +86,12 @@ export const compareWithDecimalOffset = (
}
};
function EthereumMigrationLineItem({
function EvmMigrationLineItem({
chainId,
migratorAddress,
onLoadComplete,
}: {
chainId: ChainId;
migratorAddress: string;
onLoadComplete: () => void;
}) {
@ -175,7 +174,7 @@ function EthereumMigrationLineItem({
Successfully migrated your tokens. They will become available once
this transaction confirms.
</Typography>
<ShowTx chainId={CHAIN_ID_ETH} tx={{ id: transaction, block: 1 }} />
<ShowTx chainId={chainId} tx={{ id: transaction, block: 1 }} />
</div>
</div>
);
@ -189,10 +188,7 @@ function EthereumMigrationLineItem({
<Typography className={classes.balance}>
{poolInfo.data.fromWalletBalance}
</Typography>
<SmartAddress
chainId={CHAIN_ID_ETH}
address={poolInfo.data.fromAddress}
/>
<SmartAddress chainId={chainId} address={poolInfo.data.fromAddress} />
</div>
<div>
<Typography variant="body2" color="textSecondary">
@ -207,10 +203,7 @@ function EthereumMigrationLineItem({
<Typography className={classes.balance}>
{poolInfo.data.fromWalletBalance}
</Typography>
<SmartAddress
chainId={CHAIN_ID_ETH}
address={poolInfo.data.toAddress}
/>
<SmartAddress chainId={chainId} address={poolInfo.data.toAddress} />
</div>
<div className={classes.convertButton}>
<ButtonWithLoader
@ -261,13 +254,14 @@ const getAddressBalances = async (
}
};
export default function EthereumQuickMigrate() {
export default function EvmQuickMigrate({ chainId }: { chainId: ChainId }) {
const classes = useStyles();
const { signer, signerAddress } = useEthereumProvider();
const { isReady } = useIsWalletReady(CHAIN_ID_ETH);
const { isReady } = useIsWalletReady(chainId);
const migrationMap = useMemo(() => getMigrationAssetMap(chainId), [chainId]);
const eligibleTokens = useMemo(
() => Array.from(ETH_MIGRATION_ASSET_MAP.keys()),
[]
() => Array.from(migrationMap.keys()),
[migrationMap]
);
const [migrators, setMigrators] = useState<string[] | null>(null);
const [migratorsError, setMigratorsError] = useState("");
@ -297,8 +291,7 @@ export default function EthereumQuickMigrate() {
const migratorAddresses = [];
for (const tokenAddress of result.keys()) {
if (result.get(tokenAddress) && result.get(tokenAddress)?.gt(0)) {
const migratorAddress =
ETH_MIGRATION_ASSET_MAP.get(tokenAddress);
const migratorAddress = migrationMap.get(tokenAddress);
if (migratorAddress) {
migratorAddresses.push(migratorAddress);
}
@ -323,15 +316,18 @@ export default function EthereumQuickMigrate() {
cancelled = true;
};
}
}, [isReady, signer, signerAddress, eligibleTokens]);
}, [isReady, signer, signerAddress, eligibleTokens, migrationMap]);
const hasEligibleAssets = migrators && migrators.length > 0;
const chainName = CHAINS[chainId]?.name;
const content = (
<div className={classes.containerDiv}>
<Typography variant="h5">
This page allows you to convert certain wrapped tokens on Ethereum into
Wormhole V2 tokens.
{`This page allows you to convert certain wrapped tokens ${
chainName ? "on " + chainName : ""
} into
Wormhole V2 tokens.`}
</Typography>
<EthereumSignerKey />
{!isReady ? (
@ -351,8 +347,9 @@ export default function EthereumQuickMigrate() {
<div className={classes.spacer} />
{migrators?.map((address) => {
return (
<EthereumMigrationLineItem
<EvmMigrationLineItem
key={address}
chainId={chainId}
migratorAddress={address}
onLoadComplete={reportLoadComplete}
/>

View File

@ -1,4 +1,4 @@
import { CHAIN_ID_ETH } from "@certusone/wormhole-sdk";
import { ChainId } from "@certusone/wormhole-sdk";
import {
CircularProgress,
makeStyles,
@ -27,15 +27,17 @@ const useStyles = makeStyles((theme) => ({
},
}));
export default function EthereumWorkflow({
export default function EvmWorkflow({
chainId,
migratorAddress,
}: {
chainId: ChainId;
migratorAddress: string;
}) {
const classes = useStyles();
const { enqueueSnackbar } = useSnackbar();
const { signer, signerAddress } = useEthereumProvider();
const { isReady } = useIsWalletReady(CHAIN_ID_ETH);
const { isReady } = useIsWalletReady(chainId);
const [toggleRefresh, setToggleRefresh] = useState(false);
const forceRefresh = useCallback(
() => setToggleRefresh((prevState) => !prevState),
@ -144,20 +146,20 @@ export default function EthereumWorkflow({
//TODO tokenName
const toTokenPretty = (
<SmartAddress
chainId={CHAIN_ID_ETH}
chainId={chainId}
address={poolInfo.data?.toAddress}
symbol={poolInfo.data?.toSymbol}
/>
);
const fromTokenPretty = (
<SmartAddress
chainId={CHAIN_ID_ETH}
chainId={chainId}
address={poolInfo.data?.fromAddress}
symbol={poolInfo.data?.fromSymbol}
/>
);
const poolPretty = (
<SmartAddress chainId={CHAIN_ID_ETH} address={poolInfo.data?.poolAddress} />
<SmartAddress chainId={chainId} address={poolInfo.data?.poolAddress} />
);
const fatalError = poolInfo.error
@ -218,7 +220,7 @@ export default function EthereumWorkflow({
Successfully migrated your tokens! They will be available once this
transaction confirms.
</Typography>
<ShowTx tx={{ id: transaction, block: 1 }} chainId={CHAIN_ID_ETH} />
<ShowTx tx={{ id: transaction, block: 1 }} chainId={chainId} />
</>
) : null}
</>

View File

@ -7,10 +7,7 @@ import {
} from "@material-ui/core";
import { PublicKey } from "@solana/web3.js";
import { RouteComponentProps } from "react-router-dom";
import {
ETH_MIGRATION_ASSET_MAP,
MIGRATION_ASSET_MAP,
} from "../../utils/consts";
import { getMigrationAssetMap, MIGRATION_ASSET_MAP } from "../../utils/consts";
import SolanaWorkflow from "./SolanaWorkflow";
import { withRouter } from "react-router";
import { COLORS } from "../../muiTheme";
@ -18,8 +15,9 @@ import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
CHAIN_ID_BSC,
} from "@certusone/wormhole-sdk";
import EthereumWorkflow from "./EthereumWorkflow";
import EvmWorkflow from "./EvmWorkflow";
const useStyles = makeStyles(() => ({
mainPaper: {
@ -91,7 +89,8 @@ const SolanaRoot: React.FC<Migration> = (props) => {
const EthereumRoot: React.FC<Migration> = (props) => {
const legacyAsset: string = props.match.params.legacyAsset;
const targetPool = ETH_MIGRATION_ASSET_MAP.get(legacyAsset);
const assetMap = getMigrationAssetMap(props.chainId);
const targetPool = assetMap.get(legacyAsset);
let content = null;
if (!legacyAsset || !targetPool) {
@ -101,7 +100,9 @@ const EthereumRoot: React.FC<Migration> = (props) => {
</Typography>
);
} else {
content = <EthereumWorkflow migratorAddress={targetPool} />;
content = (
<EvmWorkflow migratorAddress={targetPool} chainId={props.chainId} />
);
}
return content;
@ -113,7 +114,7 @@ const MigrationRoot: React.FC<Migration> = (props) => {
if (props.chainId === CHAIN_ID_SOLANA) {
content = <SolanaRoot {...props} />;
} else if (props.chainId === CHAIN_ID_ETH) {
} else if (props.chainId === CHAIN_ID_ETH || props.chainId === CHAIN_ID_BSC) {
content = <EthereumRoot {...props} />;
}

View File

@ -13,7 +13,7 @@ import { CovalentData } from "../../hooks/useGetSourceParsedTokenAccounts";
import { DataWrapper } from "../../store/helpers";
import { ParsedTokenAccount } from "../../store/transferSlice";
import {
ETH_MIGRATION_ASSET_MAP,
getMigrationAssetMap,
WORMHOLE_V1_ETH_ADDRESS,
} from "../../utils/consts";
import {
@ -94,8 +94,9 @@ const isWormholev1 = (provider: any, address: string, chainId: ChainId) => {
return connection.isWrappedAsset(address);
};
const isMigrationEligible = (address: string) => {
return !!ETH_MIGRATION_ASSET_MAP.get(address);
const isMigrationEligible = (chainId: ChainId, address: string) => {
const assetMap = getMigrationAssetMap(chainId);
return !!assetMap.get(address);
};
type EthereumSourceTokenSelectorProps = {
@ -110,6 +111,7 @@ type EthereumSourceTokenSelectorProps = {
};
const renderAccount = (
chainId: ChainId,
account: ParsedTokenAccount,
covalentData: CovalentData | undefined,
classes: any
@ -150,7 +152,9 @@ const renderAccount = (
</div>
);
return isMigrationEligible(account.mintKey) ? migrationRender : content;
return isMigrationEligible(chainId, account.mintKey)
? migrationRender
: content;
};
const renderNFTAccount = (
@ -534,6 +538,7 @@ export default function EthereumSourceTokenSelector(
classes
)
: renderAccount(
chainId,
option,
covalent?.data?.find(
(x) => x.contract_address === option.mintKey

View File

@ -1,4 +1,8 @@
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import {
CHAIN_ID_BSC,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
import { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
@ -21,6 +25,7 @@ import {
} from "../../store/transferSlice";
import {
BETA_CHAINS,
BSC_MIGRATION_ASSET_MAP,
CHAINS,
ETH_MIGRATION_ASSET_MAP,
MIGRATION_ASSET_MAP,
@ -56,7 +61,12 @@ function Source() {
sourceChain === CHAIN_ID_ETH &&
!!parsedTokenAccount &&
!!ETH_MIGRATION_ASSET_MAP.get(parsedTokenAccount.mintKey);
const isMigrationAsset = isSolanaMigration || isEthereumMigration;
const isBscMigration =
sourceChain === CHAIN_ID_BSC &&
!!parsedTokenAccount &&
!!BSC_MIGRATION_ASSET_MAP.get(parsedTokenAccount.mintKey);
const isMigrationAsset =
isSolanaMigration || isEthereumMigration || isBscMigration;
const uiAmountString = useSelector(selectTransferSourceBalanceString);
const amount = useSelector(selectTransferAmount);
const error = useSelector(selectTransferSourceError);
@ -70,6 +80,8 @@ function Source() {
);
} else if (sourceChain === CHAIN_ID_ETH) {
history.push(`/migrate/Ethereum/${parsedTokenAccount?.mintKey}`);
} else if (sourceChain === CHAIN_ID_BSC) {
history.push(`/migrate/BinanceSmartChain/${parsedTokenAccount?.mintKey}`);
}
}, [history, parsedTokenAccount, sourceChain]);
const handleSourceChange = useCallback(

View File

@ -5,6 +5,7 @@ import {
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
} from "@certusone/wormhole-sdk";
import { ChainID } from "@certusone/wormhole-sdk/lib/proto/publicrpc/v1/publicrpc";
import { clusterApiUrl } from "@solana/web3.js";
import { getAddress } from "ethers/lib/utils";
@ -464,4 +465,33 @@ export const ETH_MIGRATION_ASSET_MAP = new Map<string, string>(
]
);
export const BSC_MIGRATION_ASSET_MAP = new Map<string, string>(
CLUSTER === "mainnet"
? []
: CLUSTER === "testnet"
? []
: [
// [
// "0x2D8BE6BF0baA74e0A907016679CaE9190e80dD0A",
// "0x4bf3A7dFB3b76b5B3E169ACE65f888A4b4FCa5Ee",
// ],
// [
// "0x68d1569d1a6968f194b4d93f8d0b416c123a599f",
// "0xFcCeD5E997E7fb1D0594518D3eD57245bB8ed17E",
// ],
]
);
export const getMigrationAssetMap = (chainId: ChainID) => {
if (chainId === CHAIN_ID_BSC) {
return BSC_MIGRATION_ASSET_MAP;
} else if (chainId === CHAIN_ID_ETH) {
return ETH_MIGRATION_ASSET_MAP;
} else if (chainId === CHAIN_ID_SOLANA) {
return MIGRATION_ASSET_MAP;
} else {
return new Map<string, string>();
}
};
export const SUPPORTED_TERRA_TOKENS = ["uluna", "uusd"];