diff --git a/bridge_ui/src/components/SmartAddress.tsx b/bridge_ui/src/components/SmartAddress.tsx index cd858597b..3e61fe397 100644 --- a/bridge_ui/src/components/SmartAddress.tsx +++ b/bridge_ui/src/components/SmartAddress.tsx @@ -9,6 +9,7 @@ import { Button, makeStyles, Tooltip, Typography } from "@material-ui/core"; import { FileCopy, OpenInNew } from "@material-ui/icons"; import { withStyles } from "@material-ui/styles"; import clsx from "clsx"; +import { ReactChild } from "react"; import useCopyToClipboard from "../hooks/useCopyToClipboard"; import { ParsedTokenAccount } from "../store/transferSlice"; import { CLUSTER, getExplorerName } from "../utils/consts"; @@ -57,6 +58,7 @@ export default function SmartAddress({ variant, noGutter, noUnderline, + extraContent, }: { chainId: ChainId; parsedTokenAccount?: ParsedTokenAccount; @@ -67,6 +69,7 @@ export default function SmartAddress({ variant?: any; noGutter?: boolean; noUnderline?: boolean; + extraContent?: ReactChild; }) { const classes = useStyles(); const useableAddress = parsedTokenAccount?.mintKey || address || ""; @@ -148,6 +151,7 @@ export default function SmartAddress({ {explorerButton} {copyButton} + {extraContent ? extraContent : null} ); diff --git a/bridge_ui/src/components/SolanaCreateAssociatedAddress.tsx b/bridge_ui/src/components/SolanaCreateAssociatedAddress.tsx index 455b2fe73..6fe8eb5a4 100644 --- a/bridge_ui/src/components/SolanaCreateAssociatedAddress.tsx +++ b/bridge_ui/src/components/SolanaCreateAssociatedAddress.tsx @@ -5,13 +5,15 @@ import { hexToNativeString, hexToUint8Array, } from "@certusone/wormhole-sdk"; -import { Typography } from "@material-ui/core"; +import { Button, Typography } from "@material-ui/core"; +import { Alert } from "@material-ui/lab"; import { ASSOCIATED_TOKEN_PROGRAM_ID, Token, TOKEN_PROGRAM_ID, } from "@solana/spl-token"; import { Connection, PublicKey, Transaction } from "@solana/web3.js"; +import { useSnackbar } from "notistack"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useSelector } from "react-redux"; import { useSolanaWallet } from "../contexts/SolanaWalletContext"; @@ -21,6 +23,7 @@ import { selectTransferTargetAddressHex, } from "../store/selectors"; import { SOLANA_HOST, SOL_TOKEN_BRIDGE_ADDRESS } from "../utils/consts"; +import parseError from "../utils/parseError"; import { signSendAndConfirm } from "../utils/solana"; import ButtonWithLoader from "./ButtonWithLoader"; import SmartAddress from "./SmartAddress"; @@ -163,6 +166,7 @@ export default function SolanaCreateAssociatedAddress({ } export function SolanaCreateAssociatedAddressAlternate() { + const { enqueueSnackbar } = useSnackbar(); const originChain = useSelector(selectTransferOriginChain); const originAsset = useSelector(selectTransferOriginAsset); const addressHex = useSelector(selectTransferTargetAddressHex); @@ -215,7 +219,62 @@ export function SolanaCreateAssociatedAddressAlternate() { 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: ( + + Successfully created associated token account + + ), + }); + } catch (e) { + enqueueSnackbar(null, { + content: {parseError(e)}, + }); + } + } + })(); + }, [ + setAssociatedAccountExists, + targetAsset, + solPK, + base58TargetAddress, + solanaWallet, + enqueueSnackbar, + ]); + + return targetAsset ? (
Recipient Address: @@ -223,15 +282,26 @@ export function SolanaCreateAssociatedAddressAlternate() { chainId={CHAIN_ID_SOLANA} address={base58TargetAddress} variant="h6" + extraContent={ + + } /> - - + {associatedAccountExists ? null : ( + + )}
) : null; }