Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions wormhole-connect/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ import {
} from '@wormhole-foundation/sdk';

import '@wormhole-foundation/sdk/addresses';
import evm from '@wormhole-foundation/sdk/evm';
import solana from '@wormhole-foundation/sdk/solana';
import aptos from '@wormhole-foundation/sdk/aptos';
import sui from '@wormhole-foundation/sdk/sui';
import RouteOperator from 'routes/operator';
import { CHAIN_ORDER } from './constants';
import { createUiConfig } from './ui';
Expand Down Expand Up @@ -192,6 +188,11 @@ export async function newWormholeContextV2(): Promise<WormholeV2<Network>> {
v2Config.chains![chain] = { rpc, tokenMap };
}

const { default: evm } = await import('@wormhole-foundation/sdk/evm');
const { default: solana } = await import('@wormhole-foundation/sdk/solana');
const { default: aptos } = await import('@wormhole-foundation/sdk/aptos');
const { default: sui } = await import('@wormhole-foundation/sdk/sui');

return await getWormholeV2(
config.network,
[evm, solana, aptos, sui],
Expand Down
21 changes: 6 additions & 15 deletions wormhole-connect/src/utils/sdkv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ import {
} from '@wormhole-foundation/sdk';
import config from 'config';
import { NttRoute } from '@wormhole-foundation/sdk-route-ntt';
import { Connection } from '@solana/web3.js';
import { PublicKey } from '@solana/web3.js';
import * as splToken from '@solana/spl-token';
import { WORMSCAN } from 'config/constants';
import { TokenTuple } from 'config/tokens';

Expand Down Expand Up @@ -185,18 +182,12 @@ const parseTokenBridgeReceipt = async (

if (payload.to) {
if (receipt.to === 'Solana') {
if (!config.rpcs.Solana) {
throw new Error('Missing Solana RPC');
}
// the recipient on the VAA is the ATA
const ata = payload.to.address.toNative(receipt.to).toString();
const connection = new Connection(config.rpcs.Solana);
try {
const account = await splToken.getAccount(
connection,
new PublicKey(ata),
);
txData.recipient = account.owner.toBase58();
const { getAtaOwnerAddress } = await import('utils/solana');
const owner = await getAtaOwnerAddress(ata);
txData.recipient = owner;
} catch (e) {
console.error(e);
txData.recipient = ata;
Expand Down Expand Up @@ -256,10 +247,10 @@ const parseCCTPReceipt = async (
}
// the recipient on the VAA is the ATA
const ata = payload.mintRecipient.toNative(receipt.to).toString();
const connection = new Connection(config.rpcs.Solana);
try {
const account = await splToken.getAccount(connection, new PublicKey(ata));
txData.recipient = account.owner.toBase58();
const { getAtaOwnerAddress } = await import('utils/solana');
const owner = await getAtaOwnerAddress(ata);
txData.recipient = owner;
} catch (e) {
console.error(e);
txData.recipient = ata;
Expand Down
29 changes: 28 additions & 1 deletion wormhole-connect/src/utils/solana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import {
Commitment,
SimulatedTransactionResponse,
LAMPORTS_PER_SOL,
PublicKey,
} from '@solana/web3.js';
import {
getAccount as getSplAccount,
getAssociatedTokenAddressSync,
NATIVE_MINT,
} from '@solana/spl-token';

import {
determinePriorityFee,
Expand All @@ -18,7 +24,7 @@ import {
SolanaUnsignedTransaction,
} from '@wormhole-foundation/sdk-solana';

import { Network } from '@wormhole-foundation/sdk';
import { isNative, Network } from '@wormhole-foundation/sdk';
import { isEmptyObject, sleep } from 'utils';
import config from 'config';

Expand Down Expand Up @@ -289,3 +295,24 @@ function checkKnownSimulationError(
console.table(errors);
return true;
}

export function getAtaAddress(
walletAddress: string,
tokenAddress: string,
): string {
return getAssociatedTokenAddressSync(
new PublicKey(
isNative(tokenAddress) ? NATIVE_MINT : tokenAddress.toString(),
),
new PublicKey(walletAddress),
).toString();
}

export async function getAtaOwnerAddress(ataAddress: string): Promise<string> {
if (!config.rpcs.Solana) {
throw new Error('Missing Solana RPC');
}
const connection = new Connection(config.rpcs.Solana);
const account = await getSplAccount(connection, new PublicKey(ataAddress));
return account.owner.toBase58();
}
9 changes: 1 addition & 8 deletions wormhole-connect/src/utils/wallet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ import {
SuiUnsignedTransaction,
SuiChains,
} from '@wormhole-foundation/sdk-sui';
import {
AptosUnsignedTransaction,
AptosChains,
} from '@wormhole-foundation/sdk-aptos';
import { SolanaUnsignedTransaction } from '@wormhole-foundation/sdk-solana';
import { ReadOnlyWallet } from './ReadOnlyWallet';

Expand Down Expand Up @@ -254,10 +250,7 @@ export const signAndSendTransaction = async (
return tx.id;
} else if (chainConfig.context === Context.APTOS) {
const aptos = await import('utils/wallet/aptos');
const tx = await aptos.signAndSendTransaction(
request as AptosUnsignedTransaction<Network, AptosChains>,
wallet,
);
const tx = await aptos.signAndSendTransaction(request as any, wallet);
return tx.id;
} else {
throw new Error('unimplemented');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import { TransferWallet, WalletData, connectWallet } from 'utils/wallet';
import AlertBannerV2 from 'components/v2/AlertBanner';
import { useAvailableWallets } from 'hooks/useAvailableWallets';
import WalletIcon from 'icons/WalletIcons';
import { validateWalletAddress } from 'utils/address';
import { ReadOnlyWallet } from 'utils/wallet/ReadOnlyWallet';
import { SANCTIONED_WALLETS } from 'consts/wallet';

Expand Down Expand Up @@ -122,6 +121,7 @@ const WalletSidebar = (props: Props) => {
const chainConfig = config.chains[selectedChain];
if (!chainConfig) return;

const { validateWalletAddress } = await import('utils/address');
const nativeAddress = await validateWalletAddress(selectedChain, address);
if (!nativeAddress) {
setAddressError('Invalid Address');
Expand Down
94 changes: 51 additions & 43 deletions wormhole-connect/src/views/v2/Redeem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
isRefunded,
isFailed,
routes,
isNative,
} from '@wormhole-foundation/sdk';
import { getTokenDetails, getTransferDetails } from 'telemetry';
import { makeStyles } from 'tss-react/mui';
Expand Down Expand Up @@ -57,8 +56,6 @@ import type { RootState } from 'store';
import TxCompleteIcon from 'icons/TxComplete';
import TxWarningIcon from 'icons/TxWarning';
import TxFailedIcon from 'icons/TxFailed';
import { getAssociatedTokenAddressSync, NATIVE_MINT } from '@solana/spl-token';
import { PublicKey } from '@solana/web3.js';
import TxReadyForClaim from 'icons/TxReadyForClaim';
import { useGetRedeemTokens } from 'hooks/useGetTokens';
import { tokenIdFromTuple } from 'config/tokens';
Expand Down Expand Up @@ -138,6 +135,8 @@ const Redeem = () => {

const [claimError, setClaimError] = useState('');
const [isClaimInProgress, setIsClaimInProgress] = useState(false);
const [isConnectedToReceivingWallet, setIsConnectedToReceivingWallet] =
useState(false);
const [transferSuccessEventFired, setTransferSuccessEventFired] =
useState(false);
const [etaExpired, setEtaExpired] = useState(false);
Expand Down Expand Up @@ -639,53 +638,62 @@ const Redeem = () => {
}, [routeContext.receipt]);

// Checks whether the receiving wallet is currently connected
const isConnectedToReceivingWallet = useMemo(() => {
useEffect(() => {
let canceled = false;
const rv = () => {
canceled = true;
};

if (!recipient) {
return false;
return rv;
}

// For Solana transfers, the associated token account (ATA) might not exist,
// preventing us from retrieving the recipient wallet address.
// In such cases, when resuming transfers, we allow the user to connect a wallet
// to claim the transfer, which will create the ATA.
if (
isResumeTx &&
toChain === 'Solana' &&
receivingWallet.address &&
receivingWallet.type === Context.SOLANA &&
receivingWallet.address !== recipient &&
routeName &&
// These routes set the recipient address to the associated token address
['ManualTokenBridge', 'ManualCCTP'].includes(routeName)
) {
const { address: receiveTokenAddress } = tokenIdFromTuple(receivedToken);

const ata = getAssociatedTokenAddressSync(
new PublicKey(
isNative(receiveTokenAddress)
? NATIVE_MINT
: receiveTokenAddress.toString(),
),
new PublicKey(receivingWallet.address),
);
if (!ata.equals(new PublicKey(recipient))) {
setClaimError('Not connected to the receiving wallet');
return false;
const check = async () => {
if (
isResumeTx &&
toChain === 'Solana' &&
receivingWallet.address &&
receivingWallet.type === Context.SOLANA &&
receivingWallet.address !== recipient &&
routeName &&
['ManualTokenBridge', 'ManualCCTP'].includes(routeName)
) {
const { address: receiveTokenAddress } =
tokenIdFromTuple(receivedToken);

const { getAtaAddress } = await import('utils/solana');
const ata = getAtaAddress(
receivingWallet.address,
receiveTokenAddress.toString(),
);
if (ata != recipient) {
if (!canceled) {
setClaimError('Not connected to the receiving wallet');
setIsConnectedToReceivingWallet(false);
}
}

if (!canceled) {
setClaimError('');
setIsConnectedToReceivingWallet(true);
}
}

setClaimError('');
return true;
}
const walletAddress = receivingWallet.address.toLowerCase();
const walletCurrentAddress = receivingWallet.currentAddress.toLowerCase();
const recipientAddress = recipient.toLowerCase();

if (!canceled) {
setIsConnectedToReceivingWallet(
walletAddress === walletCurrentAddress &&
walletAddress === recipientAddress,
);
}
};

const walletAddress = receivingWallet.address.toLowerCase();
const walletCurrentAddress = receivingWallet.currentAddress.toLowerCase();
const recipientAddress = recipient.toLowerCase();
check();

// Connected wallet should be the current recipient wallet
return (
walletAddress === walletCurrentAddress &&
walletAddress === recipientAddress
);
return rv;
}, [
receivingWallet,
recipient,
Expand Down