How to interact with the wallet

Sign messages, send transactions, and stake SOL. The interactive demo below lets you stake real SOL to a validator.

Real Transactions

This demo submits real transactions to Solana mainnet. Staked SOL will be delegated to your chosen validator. Make sure you understand the warmup and cooldown periods before staking.

Staking Demo

Connect your wallet to stake SOL to any Solana validator. This demo uses Phantom's signAndSendTransaction method.

Sign a Message

Use signMessage for wallet verification without creating an on-chain transaction.

import { useSolana } from '@phantom/react-sdk';

function SignMessageButton() {
const { solana, isAvailable } = useSolana();

const signMessage = async () => {
if (!isAvailable || !solana) {
throw new Error('Phantom not available');
}

const message = 'Sign this message to verify your wallet';
const encodedMessage = new TextEncoder().encode(message);

const { signature, publicKey } = await solana.signMessage(
encodedMessage,
'utf8'
);

console.log('Signature:', signature);
console.log('Signed by:', publicKey.toString());

// Verify on server with tweetnacl or @solana/web3.js
return { signature, publicKey: publicKey.toString() };
};

return <button onClick={signMessage}>Sign Message</button>;
}

Sign and Send Transaction

Build a transaction, then use Phantom to sign and submit it to the network.

import { useSolana } from '@phantom/react-sdk';
import {
Connection,
PublicKey,
SystemProgram,
TransactionMessage,
VersionedTransaction,
} from '@solana/web3.js';

function SendSolButton({ recipient, amount }: { recipient: string; amount: number }) {
const { solana, isAvailable } = useSolana();

const sendSol = async () => {
if (!isAvailable || !solana?.publicKey) {
throw new Error('Wallet not connected');
}

const connection = new Connection(
'https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY'
);

// Get recent blockhash
const { blockhash } = await connection.getLatestBlockhash();

// Build VersionedTransaction (required by Phantom Connect SDK)
const messageV0 = new TransactionMessage({
payerKey: solana.publicKey,
recentBlockhash: blockhash,
instructions: [
SystemProgram.transfer({
fromPubkey: solana.publicKey,
toPubkey: new PublicKey(recipient),
lamports: amount * 1e9, // Convert SOL to lamports
}),
],
}).compileToV0Message();

const transaction = new VersionedTransaction(messageV0);

// Sign and send via Phantom SDK
const { signature } = await solana.signAndSendTransaction(transaction);

console.log('Transaction sent:', signature);
return signature;
};

return <button onClick={sendSol}>Send {amount} SOL</button>;
}

Build a Stake Transaction

The staking demo above uses this code to build and submit stake transactions.

import { useSolana } from '@phantom/react-sdk';
import {
Connection,
PublicKey,
StakeProgram,
SystemProgram,
TransactionMessage,
VersionedTransaction,
} from '@solana/web3.js';

function StakeButton({ validator, amount }: { validator: string; amount: number }) {
const { solana, isAvailable } = useSolana();

const stake = async () => {
if (!isAvailable || !solana?.publicKey) {
throw new Error('Wallet not connected');
}

const walletPubkey = solana.publicKey;
const validatorVotePubkey = new PublicKey(validator);
const amountLamports = amount * 1_000_000_000;
const rentExemption = 2282880; // ~0.00228 SOL

// Derive stake account from wallet + seed (single-signer, no Keypair needed)
const seed = `stake:${Date.now().toString(36)}`;
const stakeAccountPubkey = await PublicKey.createWithSeed(
walletPubkey, seed, StakeProgram.programId
);

const connection = new Connection(
'https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY'
);
const { blockhash } = await connection.getLatestBlockhash();

// Build instructions using createAccountWithSeed (only wallet signs)
const instructions = [
SystemProgram.createAccountWithSeed({
fromPubkey: walletPubkey,
newAccountPubkey: stakeAccountPubkey,
basePubkey: walletPubkey,
seed,
lamports: amountLamports + rentExemption,
space: 200,
programId: StakeProgram.programId,
}),
StakeProgram.initialize({
stakePubkey: stakeAccountPubkey,
authorized: { staker: walletPubkey, withdrawer: walletPubkey },
}),
...StakeProgram.delegate({
stakePubkey: stakeAccountPubkey,
authorizedPubkey: walletPubkey,
votePubkey: validatorVotePubkey,
}).instructions,
];

// Build VersionedTransaction (required by Phantom Connect SDK)
const messageV0 = new TransactionMessage({
payerKey: walletPubkey,
recentBlockhash: blockhash,
instructions,
}).compileToV0Message();

const transaction = new VersionedTransaction(messageV0);

// Sign and send via Phantom SDK (wallet is only signer)
const { signature } = await solana.signAndSendTransaction(transaction);
console.log('Staked! Signature:', signature);
return signature;
};

return <button onClick={stake}>Stake {amount} SOL</button>;
}

Frequently Asked Questions

How do I sign a transaction with Phantom?
Use signAndSendTransaction() to sign and broadcast in one step, or signTransaction() to get the signed transaction back for manual submission.
What is the difference between signMessage and signTransaction?
signMessage signs arbitrary data (like login verification) without creating a blockchain transaction. signTransaction signs an actual Solana transaction that will modify state on-chain.
How do I stake SOL programmatically?
Build a stake transaction using @solana-program/stake, then use Phantom's signAndSendTransaction to submit it. See the interactive demo above.
Can I simulate transactions before signing?
Yes, use the simulateTransaction RPC method before signing. This catches errors and estimates fees without submitting to the network.