Prerequisites
You need an authenticated Para client and basic knowledge of Solana transactions.Core Dependencies
- Solana Web3.js
- Solana Signers v2
- Coral Anchor
npm install @getpara/react-sdk @getpara/solana-web3-integration @solana/web3.js
npm install @getpara/react-sdk @getpara/solana-signers-v2-integration @solana/kit @solana/transactions @solana-program/system
npm install @getpara/react-sdk @getpara/solana-web3-integration @solana/web3.js @coral-xyz/anchor
Connection Setup
RPC Configuration
- Solana Web3.js
- Solana Signers v2
- Coral Anchor
import { Connection } from "@solana/web3.js";
const DEVNET_RPC_URL = "https://api.devnet.solana.com";
// Create Solana connection
const connection = new Connection(DEVNET_RPC_URL, "confirmed");
import { createHttpTransport } from "@solana/rpc-spec";
import { createSolanaRpc } from "@solana/kit";
const DEVNET_RPC_URL = "https://api.devnet.solana.com";
// Create HTTP transport
const transport = createHttpTransport({ url: DEVNET_RPC_URL });
// Create Kit RPC client
const kitRpc = createSolanaRpc({ transport });
// Create Para RPC client
const paraRpc = createHttpTransport({ url: DEVNET_RPC_URL });
import { Connection } from "@solana/web3.js";
import { AnchorProvider } from "@coral-xyz/anchor";
const DEVNET_RPC_URL = "https://api.devnet.solana.com";
// Create Solana connection
const connection = new Connection(DEVNET_RPC_URL, "confirmed");
Para Signer Setup
- Solana Web3.js
- Solana Signers v2
- Coral Anchor
import { ParaSolanaWeb3Signer } from "@getpara/solana-web3-integration";
import { useAccount, useClient } from "@getpara/react-sdk";
// Get Para client and account
const client = useClient();
const { data: account } = useAccount();
// Create Para Solana signer
let signer: ParaSolanaWeb3Signer | null = null;
if (account?.isConnected && connection && client) {
signer = new ParaSolanaWeb3Signer(client, connection);
}
import { createParaSolanaSigner } from "@getpara/solana-signers-v2-integration";
import { useAccount, useClient } from "@getpara/react-sdk";
// Get Para client and account
const client = useClient();
const { data: account } = useAccount();
// Create Para Solana signer
let signer: any = null;
if (account?.isConnected && paraRpc && client) {
signer = createParaSolanaSigner({
para: client,
rpc: paraRpc
});
}
import { ParaSolanaWeb3Signer } from "@getpara/solana-web3-integration";
import { AnchorProvider } from "@coral-xyz/anchor";
import { useAccount, useClient } from "@getpara/react-sdk";
// Get Para client and account
const client = useClient();
const { data: account } = useAccount();
// Create Para Solana signer and Anchor provider
let anchorProvider: AnchorProvider | null = null;
if (account?.isConnected && connection && client) {
const signer = new ParaSolanaWeb3Signer(client, connection);
// Create wallet adapter for Anchor
const walletAdapter = {
publicKey: signer.sender,
signTransaction: async (tx: any) => await signer.signTransaction(tx),
signAllTransactions: async (txs: any[]) => await signer.signAllTransactions(txs)
};
anchorProvider = new AnchorProvider(connection, walletAdapter, {
commitment: "confirmed"
});
}
Transaction Construction
Basic SOL Transfer
- Solana Web3.js
- Solana Signers v2
- Coral Anchor
import {
Transaction,
SystemProgram,
PublicKey,
LAMPORTS_PER_SOL
} from "@solana/web3.js";
const constructTransaction = async (
toAddress: string,
solAmount: string,
fromPublicKey: PublicKey
) => {
const toPubKey = new PublicKey(toAddress);
const amountLamports = parseFloat(solAmount) * LAMPORTS_PER_SOL;
// Create transaction
const transaction = new Transaction();
// Add transfer instruction
transaction.add(
SystemProgram.transfer({
fromPubkey: fromPublicKey,
toPubkey: toPubKey,
lamports: BigInt(amountLamports)
})
);
// Set transaction parameters
const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = fromPublicKey;
return transaction;
};
import { pipe } from "@solana/transactions";
import {
getTransferSolInstruction,
lamports,
address
} from "@solana-program/system";
import {
appendTransactionMessageInstruction,
createTransactionMessage,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash
} from "@solana/transactions";
const constructTransaction = async (
toAddress: string,
solAmount: string,
signerAddress: any
) => {
// Get latest blockhash
const { value: latestBlockhash } = await kitRpc.getLatestBlockhash().send();
// Convert SOL to lamports
const amountLamports = BigInt(parseFloat(solAmount) * 1e9);
// Create transfer instruction
const transferInstruction = getTransferSolInstruction({
source: signerAddress,
destination: address(toAddress),
amount: lamports(amountLamports)
});
// Build transaction message
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
tx => setTransactionMessageFeePayerSigner(signerAddress, tx),
tx => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash.blockhash, tx),
tx => appendTransactionMessageInstruction(transferInstruction, tx)
);
return transactionMessage;
};
import {
Transaction,
SystemProgram,
PublicKey,
LAMPORTS_PER_SOL
} from "@solana/web3.js";
const constructTransaction = async (
toAddress: string,
solAmount: string,
walletPublicKey: PublicKey
) => {
const toPubKey = new PublicKey(toAddress);
const amountLamports = parseFloat(solAmount) * LAMPORTS_PER_SOL;
// Create transaction
const transaction = new Transaction();
// Add transfer instruction
transaction.add(
SystemProgram.transfer({
fromPubkey: walletPublicKey,
toPubkey: toPubKey,
lamports: BigInt(amountLamports)
})
);
// Set transaction parameters
const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = walletPublicKey;
return transaction;
};
Balance Validation
Check Sufficient Balance
- Solana Web3.js
- Solana Signers v2
- Coral Anchor
const validateTransaction = async (
userPublicKey: PublicKey,
solAmount: string
) => {
// Get current balance
const balanceLamports = await connection.getBalance(userPublicKey);
// Calculate amount in lamports
const amountLamports = parseFloat(solAmount) * LAMPORTS_PER_SOL;
// Estimate transaction fee (5000 lamports is typical)
const estimatedFee = 5000;
const totalCost = amountLamports + estimatedFee;
if (totalCost > balanceLamports) {
throw new Error(
`Insufficient balance. Need ${totalCost / LAMPORTS_PER_SOL} SOL, have ${balanceLamports / LAMPORTS_PER_SOL} SOL`
);
}
return true;
};
const validateTransaction = async (
signerAddress: any,
solAmount: string
) => {
// Get current balance
const { value: balance } = await kitRpc.getBalance(signerAddress).send();
// Calculate amount in lamports
const amountLamports = BigInt(parseFloat(solAmount) * 1e9);
// Estimate transaction fee
const estimatedFee = BigInt(5000);
const totalCost = amountLamports + estimatedFee;
if (totalCost > balance) {
throw new Error(
`Insufficient balance. Need ${Number(totalCost) / 1e9} SOL, have ${Number(balance) / 1e9} SOL`
);
}
return true;
};
const validateTransaction = async (
walletPublicKey: PublicKey,
solAmount: string
) => {
// Get current balance
const balanceLamports = await connection.getBalance(walletPublicKey);
// Calculate amount in lamports
const amountLamports = parseFloat(solAmount) * LAMPORTS_PER_SOL;
// Estimate transaction fee
const estimatedFee = 5000;
const totalCost = amountLamports + estimatedFee;
if (totalCost > balanceLamports) {
throw new Error(
`Insufficient balance. Need ${totalCost / LAMPORTS_PER_SOL} SOL, have ${balanceLamports / LAMPORTS_PER_SOL} SOL`
);
}
return true;
};
Sending Transactions
Execute Transfer
- Solana Web3.js
- Solana Signers v2
- Coral Anchor
const sendSolTransfer = async (
toAddress: string,
solAmount: string,
fromPublicKey: PublicKey
) => {
// Validate transaction
await validateTransaction(fromPublicKey, solAmount);
// Construct transaction
const transaction = await constructTransaction(toAddress, solAmount, fromPublicKey);
// Send transaction using Para signer
const txSignature = await signer.sendTransaction(transaction);
console.log("Transaction sent:", txSignature);
// Wait for confirmation with polling
let receipt = null;
while (!receipt) {
receipt = await connection.getSignatureStatus(txSignature, {
searchTransactionHistory: true
});
if (receipt?.value?.confirmationStatus === "confirmed" ||
receipt?.value?.confirmationStatus === "finalized") {
break;
}
// Wait 500ms before next check
await new Promise(resolve => setTimeout(resolve, 500));
}
console.log("Transaction confirmed:", receipt);
return {
signature: txSignature,
status: receipt?.value?.confirmationStatus,
slot: receipt?.value?.slot
};
};
import {
compileTransaction,
getBase64EncodedWireTransaction
} from "@solana/transactions";
const sendSolTransfer = async (
toAddress: string,
solAmount: string,
signerAddress: any
) => {
// Validate transaction
await validateTransaction(signerAddress, solAmount);
// Construct transaction message
const transactionMessage = await constructTransaction(toAddress, solAmount, signerAddress);
// Sign transaction
const [signedTransaction] = await signer.modifyAndSignTransactions([
compileTransaction(transactionMessage)
]);
// Serialize transaction
const serializedTransaction = getBase64EncodedWireTransaction(signedTransaction);
// Send transaction
const { value: signature } = await kitRpc.sendTransaction(
serializedTransaction,
{
encoding: "base64",
skipPreflight: false,
preflightCommitment: "confirmed"
}
).send();
console.log("Transaction sent:", signature);
// Wait for confirmation
const { value: statuses } = await kitRpc.getSignatureStatuses([signature]).send();
const status = statuses[0];
console.log("Transaction confirmed:", status);
return {
signature,
status: status?.confirmationStatus,
slot: status?.slot
};
};
const sendSolTransfer = async (
toAddress: string,
solAmount: string,
walletPublicKey: PublicKey
) => {
// Validate transaction
await validateTransaction(walletPublicKey, solAmount);
// Construct transaction
const transaction = await constructTransaction(toAddress, solAmount, walletPublicKey);
// Send and confirm transaction using Anchor provider
const txSignature = await anchorProvider.sendAndConfirm(transaction);
console.log("Transaction sent and confirmed:", txSignature);
return {
signature: txSignature,
status: "confirmed"
};
};
Complete Implementation Example
Full Transfer Service
- Solana Web3.js
- Solana Signers v2
- Coral Anchor
import {
Connection,
Transaction,
SystemProgram,
PublicKey,
LAMPORTS_PER_SOL
} from "@solana/web3.js";
import { ParaSolanaWeb3Signer } from "@getpara/solana-web3-integration";
class SolanaWeb3TransferService {
private connection: Connection;
private signer: ParaSolanaWeb3Signer;
constructor(rpcUrl: string, paraClient: any) {
this.connection = new Connection(rpcUrl, "confirmed");
this.signer = new ParaSolanaWeb3Signer(paraClient, this.connection);
}
async transfer(toAddress: string, solAmount: string) {
try {
const fromPublicKey = this.signer.sender;
// Validate balance
const balanceLamports = await this.connection.getBalance(fromPublicKey);
const amountLamports = parseFloat(solAmount) * LAMPORTS_PER_SOL;
const estimatedFee = 5000;
if (balanceLamports < amountLamports + estimatedFee) {
throw new Error("Insufficient balance");
}
// Create transaction
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromPublicKey,
toPubkey: new PublicKey(toAddress),
lamports: BigInt(amountLamports)
})
);
const { blockhash } = await this.connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = fromPublicKey;
// Send transaction
const signature = await this.signer.sendTransaction(transaction);
// Wait for confirmation
await this.connection.confirmTransaction(signature);
return { success: true, signature };
} catch (error) {
console.error("Transfer failed:", error);
throw error;
}
}
}
import { pipe } from "@solana/transactions";
import { createSolanaRpc, createHttpTransport } from "@solana/kit";
import { getTransferSolInstruction, lamports, address } from "@solana-program/system";
import { createParaSolanaSigner } from "@getpara/solana-signers-v2-integration";
class SolanaSignersTransferService {
private kitRpc: any;
private signer: any;
constructor(rpcUrl: string, paraClient: any) {
const transport = createHttpTransport({ url: rpcUrl });
this.kitRpc = createSolanaRpc({ transport });
this.signer = createParaSolanaSigner({
para: paraClient,
rpc: createHttpTransport({ url: rpcUrl })
});
}
async transfer(toAddress: string, solAmount: string) {
try {
const signerAddress = this.signer.address;
// Validate balance
const { value: balance } = await this.kitRpc.getBalance(signerAddress).send();
const amountLamports = BigInt(parseFloat(solAmount) * 1e9);
const estimatedFee = BigInt(5000);
if (balance < amountLamports + estimatedFee) {
throw new Error("Insufficient balance");
}
// Get latest blockhash
const { value: latestBlockhash } = await this.kitRpc.getLatestBlockhash().send();
// Create transfer instruction
const transferInstruction = getTransferSolInstruction({
source: signerAddress,
destination: address(toAddress),
amount: lamports(amountLamports)
});
// Build and sign transaction
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
tx => setTransactionMessageFeePayerSigner(signerAddress, tx),
tx => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash.blockhash, tx),
tx => appendTransactionMessageInstruction(transferInstruction, tx)
);
const [signedTransaction] = await this.signer.modifyAndSignTransactions([
compileTransaction(transactionMessage)
]);
// Send transaction
const serialized = getBase64EncodedWireTransaction(signedTransaction);
const { value: signature } = await this.kitRpc.sendTransaction(serialized).send();
return { success: true, signature };
} catch (error) {
console.error("Transfer failed:", error);
throw error;
}
}
}
import {
Connection,
Transaction,
SystemProgram,
PublicKey,
LAMPORTS_PER_SOL
} from "@solana/web3.js";
import { AnchorProvider } from "@coral-xyz/anchor";
import { ParaSolanaWeb3Signer } from "@getpara/solana-web3-integration";
class AnchorTransferService {
private connection: Connection;
private anchorProvider: AnchorProvider;
constructor(rpcUrl: string, paraClient: any) {
this.connection = new Connection(rpcUrl, "confirmed");
const signer = new ParaSolanaWeb3Signer(paraClient, this.connection);
const walletAdapter = {
publicKey: signer.sender,
signTransaction: async (tx: any) => await signer.signTransaction(tx),
signAllTransactions: async (txs: any[]) => await signer.signAllTransactions(txs)
};
this.anchorProvider = new AnchorProvider(this.connection, walletAdapter, {
commitment: "confirmed"
});
}
async transfer(toAddress: string, solAmount: string) {
try {
const fromPublicKey = this.anchorProvider.wallet.publicKey;
// Validate balance
const balanceLamports = await this.connection.getBalance(fromPublicKey);
const amountLamports = parseFloat(solAmount) * LAMPORTS_PER_SOL;
const estimatedFee = 5000;
if (balanceLamports < amountLamports + estimatedFee) {
throw new Error("Insufficient balance");
}
// Create transaction
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromPublicKey,
toPubkey: new PublicKey(toAddress),
lamports: BigInt(amountLamports)
})
);
const { blockhash } = await this.connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = fromPublicKey;
// Send and confirm transaction
const signature = await this.anchorProvider.sendAndConfirm(transaction);
return { success: true, signature };
} catch (error) {
console.error("Transfer failed:", error);
throw error;
}
}
}
Key Differences
Library Comparison
| Feature | Solana Web3.js | Solana Signers v2 | Coral Anchor |
|---|---|---|---|
| Status | Legacy | Modern/Recommended | Smart Contracts |
| Transaction Type | Transaction | TransactionMessage | Transaction |
| Instruction Creation | SystemProgram.transfer() | getTransferSolInstruction() | SystemProgram.transfer() |
| Signing | signer.sendTransaction() | signer.modifyAndSignTransactions() | provider.sendAndConfirm() |
| Amount Format | BigInt(lamports) | lamports(bigint) | BigInt(lamports) |
| Confirmation | Manual polling | Status check | Automatic |
Transaction Construction Patterns
- Solana Web3.js
- Solana Signers v2
- Coral Anchor
// Legacy approach with Transaction class
const transaction = new Transaction();
transaction.add(SystemProgram.transfer({...}));
transaction.recentBlockhash = blockhash;
transaction.feePayer = publicKey;
// Modern functional approach with pipes
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
tx => setTransactionMessageFeePayerSigner(signer, tx),
tx => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),
tx => appendTransactionMessageInstruction(instruction, tx)
);
// Anchor provider abstraction
const transaction = new Transaction();
transaction.add(SystemProgram.transfer({...}));
// Provider handles blockhash and fee payer automatically
const signature = await provider.sendAndConfirm(transaction);
Best Practices
Library Selection
- Solana Web3.js: Use for compatibility with existing projects, but consider migrating
- Solana Signers v2: Recommended for new projects, modern patterns, better performance
- Coral Anchor: Best for dApps interacting with Anchor-based smart contracts
Transaction Safety
- Always validate recipient addresses using
PublicKeyconstructor - Check balance including fees before transaction construction
- Use appropriate commitment levels (“confirmed” for most cases)
- Handle network errors gracefully with proper retry logic
- Monitor transaction confirmation rather than assuming success
Performance Optimization
- Reuse connection instances rather than creating new ones
- Batch multiple transfers when possible
- Use appropriate RPC endpoints for your use case
- Monitor transaction fees and adjust as needed
Next Steps
SPL Token Transfers
Learn to transfer SPL tokens on Solana
Solana Programs
Interact with Solana smart contracts
Related Walkthroughs
Ethereum Transfers
Intermediate · 20 min · Send ETH with Ethers and Viem
Squid Router Integration
Advanced · 45 min · Cross-chain USDC bridges
Bulk Wallet Pregeneration
Intermediate · 25 min · Large-scale wallet generation