bridge_ui: allow force create account on redeem

Change-Id: I6f01fd350dd18339ba0b5659ab43bd5c2a2a22c7
This commit is contained in:
Evan Gray 2021-10-19 17:27:13 -04:00
parent fb030b3351
commit 9bc408ca19
2 changed files with 83 additions and 9 deletions

View File

@ -9,6 +9,7 @@ import { Button, makeStyles, Tooltip, Typography } from "@material-ui/core";
import { FileCopy, OpenInNew } from "@material-ui/icons"; import { FileCopy, OpenInNew } from "@material-ui/icons";
import { withStyles } from "@material-ui/styles"; import { withStyles } from "@material-ui/styles";
import clsx from "clsx"; import clsx from "clsx";
import { ReactChild } from "react";
import useCopyToClipboard from "../hooks/useCopyToClipboard"; import useCopyToClipboard from "../hooks/useCopyToClipboard";
import { ParsedTokenAccount } from "../store/transferSlice"; import { ParsedTokenAccount } from "../store/transferSlice";
import { CLUSTER, getExplorerName } from "../utils/consts"; import { CLUSTER, getExplorerName } from "../utils/consts";
@ -57,6 +58,7 @@ export default function SmartAddress({
variant, variant,
noGutter, noGutter,
noUnderline, noUnderline,
extraContent,
}: { }: {
chainId: ChainId; chainId: ChainId;
parsedTokenAccount?: ParsedTokenAccount; parsedTokenAccount?: ParsedTokenAccount;
@ -67,6 +69,7 @@ export default function SmartAddress({
variant?: any; variant?: any;
noGutter?: boolean; noGutter?: boolean;
noUnderline?: boolean; noUnderline?: boolean;
extraContent?: ReactChild;
}) { }) {
const classes = useStyles(); const classes = useStyles();
const useableAddress = parsedTokenAccount?.mintKey || address || ""; const useableAddress = parsedTokenAccount?.mintKey || address || "";
@ -148,6 +151,7 @@ export default function SmartAddress({
{explorerButton} {explorerButton}
{copyButton} {copyButton}
</div> </div>
{extraContent ? extraContent : null}
</> </>
); );

View File

@ -5,13 +5,15 @@ import {
hexToNativeString, hexToNativeString,
hexToUint8Array, hexToUint8Array,
} from "@certusone/wormhole-sdk"; } from "@certusone/wormhole-sdk";
import { Typography } from "@material-ui/core"; import { Button, Typography } from "@material-ui/core";
import { Alert } from "@material-ui/lab";
import { import {
ASSOCIATED_TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID,
Token, Token,
TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID,
} from "@solana/spl-token"; } from "@solana/spl-token";
import { Connection, PublicKey, Transaction } from "@solana/web3.js"; import { Connection, PublicKey, Transaction } from "@solana/web3.js";
import { useSnackbar } from "notistack";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { useSolanaWallet } from "../contexts/SolanaWalletContext"; import { useSolanaWallet } from "../contexts/SolanaWalletContext";
@ -21,6 +23,7 @@ import {
selectTransferTargetAddressHex, selectTransferTargetAddressHex,
} from "../store/selectors"; } from "../store/selectors";
import { SOLANA_HOST, SOL_TOKEN_BRIDGE_ADDRESS } from "../utils/consts"; import { SOLANA_HOST, SOL_TOKEN_BRIDGE_ADDRESS } from "../utils/consts";
import parseError from "../utils/parseError";
import { signSendAndConfirm } from "../utils/solana"; import { signSendAndConfirm } from "../utils/solana";
import ButtonWithLoader from "./ButtonWithLoader"; import ButtonWithLoader from "./ButtonWithLoader";
import SmartAddress from "./SmartAddress"; import SmartAddress from "./SmartAddress";
@ -163,6 +166,7 @@ export default function SolanaCreateAssociatedAddress({
} }
export function SolanaCreateAssociatedAddressAlternate() { export function SolanaCreateAssociatedAddressAlternate() {
const { enqueueSnackbar } = useSnackbar();
const originChain = useSelector(selectTransferOriginChain); const originChain = useSelector(selectTransferOriginChain);
const originAsset = useSelector(selectTransferOriginAsset); const originAsset = useSelector(selectTransferOriginAsset);
const addressHex = useSelector(selectTransferTargetAddressHex); const addressHex = useSelector(selectTransferTargetAddressHex);
@ -215,7 +219,62 @@ export function SolanaCreateAssociatedAddressAlternate() {
base58TargetAddress base58TargetAddress
); );
return targetAsset && !associatedAccountExists ? ( const solanaWallet = useSolanaWallet();
const solPK = solanaWallet?.publicKey;
const handleForceCreateClick = useCallback(() => {
if (!targetAsset || !base58TargetAddress || !solPK) return;
(async () => {
const connection = new Connection(SOLANA_HOST, "confirmed");
const mintPublicKey = new PublicKey(targetAsset);
const payerPublicKey = new PublicKey(solPK); // currently assumes the wallet is the owner
const associatedAddress = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
mintPublicKey,
payerPublicKey
);
const match = associatedAddress.toString() === base58TargetAddress;
if (match) {
try {
const transaction = new Transaction().add(
await Token.createAssociatedTokenAccountInstruction(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
mintPublicKey,
associatedAddress,
payerPublicKey, // owner
payerPublicKey // payer
)
);
const { blockhash } = await connection.getRecentBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(payerPublicKey);
await signSendAndConfirm(solanaWallet, connection, transaction);
setAssociatedAccountExists(true);
enqueueSnackbar(null, {
content: (
<Alert severity="success">
Successfully created associated token account
</Alert>
),
});
} catch (e) {
enqueueSnackbar(null, {
content: <Alert severity="error">{parseError(e)}</Alert>,
});
}
}
})();
}, [
setAssociatedAccountExists,
targetAsset,
solPK,
base58TargetAddress,
solanaWallet,
enqueueSnackbar,
]);
return targetAsset ? (
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<Typography variant="subtitle2">Recipient Address:</Typography> <Typography variant="subtitle2">Recipient Address:</Typography>
<Typography component="div"> <Typography component="div">
@ -223,15 +282,26 @@ export function SolanaCreateAssociatedAddressAlternate() {
chainId={CHAIN_ID_SOLANA} chainId={CHAIN_ID_SOLANA}
address={base58TargetAddress} address={base58TargetAddress}
variant="h6" variant="h6"
extraContent={
<Button
size="small"
variant="outlined"
onClick={handleForceCreateClick}
disabled={!targetAsset || !base58TargetAddress || !solPK}
>
Force Create Account
</Button>
}
/> />
</Typography> </Typography>
{associatedAccountExists ? null : (
<SolanaCreateAssociatedAddress <SolanaCreateAssociatedAddress
mintAddress={targetAsset} mintAddress={targetAsset}
readableTargetAddress={base58TargetAddress} readableTargetAddress={base58TargetAddress}
associatedAccountExists={associatedAccountExists} associatedAccountExists={associatedAccountExists}
setAssociatedAccountExists={setAssociatedAccountExists} setAssociatedAccountExists={setAssociatedAccountExists}
/> />
)}
</div> </div>
) : null; ) : null;
} }