Transaction Submission
Complete guide to sending, simulating, and monitoring blockchain transactions with best practices for reliability
Transaction Submission
Transaction submission is the process of sending signed transactions to the X1 Blockchain. This guide covers everything from building transactions to handling failures and ensuring successful execution.
Overview
Submitting transactions involves several steps:
- Building - Construct transaction with instructions and accounts
- Simulating - Test transaction before sending (optional but recommended)
- Signing - Sign with required private keys
- Sending - Submit to the network
- Confirming - Wait for confirmation and verify success
When to Use Transaction Methods
Use these methods when you need to:
- Send SOL transfers - Move native tokens between accounts
- Execute program instructions - Call on-chain programs
- Create or modify accounts - Deploy programs or initialize data
- Test transactions - Simulate before actual submission
Core Methods
sendTransaction
Submits a fully-signed transaction to the blockchain.
const {
Connection,
Keypair,
SystemProgram,
Transaction,
sendAndConfirmTransaction,
PublicKey,
LAMPORTS_PER_SOL
} = require('@solana/web3.js');
const connection = new Connection(
'https://nexus.fortiblox.com/rpc',
{
commitment: 'confirmed',
httpHeaders: {
'X-API-Key': process.env.FORTIBLOX_API_KEY
}
}
);
async function sendTransaction(fromKeypair, toAddress, amountSOL) {
const toPubkey = new PublicKey(toAddress);
// Create transaction
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: toPubkey,
lamports: amountSOL * LAMPORTS_PER_SOL
})
);
// Get recent blockhash
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = fromKeypair.publicKey;
// Sign transaction
transaction.sign(fromKeypair);
// Send transaction
const signature = await connection.sendRawTransaction(
transaction.serialize()
);
console.log('Transaction sent:', signature);
// Wait for confirmation
const confirmation = await connection.confirmTransaction({
signature,
blockhash,
lastValidBlockHeight
});
if (confirmation.value.err) {
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`);
}
console.log('Transaction confirmed:', signature);
return signature;
}
// Helper: Send with automatic confirmation
async function sendAndConfirm(fromKeypair, toAddress, amountSOL) {
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: new PublicKey(toAddress),
lamports: amountSOL * LAMPORTS_PER_SOL
})
);
const signature = await sendAndConfirmTransaction(
connection,
transaction,
[fromKeypair]
);
console.log('Transaction confirmed:', signature);
return signature;
}
// Example usage
const sender = Keypair.fromSecretKey(/* your secret key */);
sendTransaction(
sender,
'vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg',
0.1
);from solana.rpc.api import Client
from solana.transaction import Transaction
from solana.system_program import TransferParams, transfer
from solana.keypair import Keypair
from solana.publickey import PublicKey
import os
client = Client(
"https://nexus.fortiblox.com/rpc",
extra_headers={"X-API-Key": os.environ['FORTIBLOX_API_KEY']}
)
def send_transaction(from_keypair, to_address, amount_sol):
"""Send SOL from one account to another"""
to_pubkey = PublicKey(to_address)
# Create transfer instruction
transfer_ix = transfer(
TransferParams(
from_pubkey=from_keypair.public_key,
to_pubkey=to_pubkey,
lamports=int(amount_sol * 1e9)
)
)
# Create transaction
transaction = Transaction()
transaction.add(transfer_ix)
# Get recent blockhash
blockhash_resp = client.get_latest_blockhash()
transaction.recent_blockhash = blockhash_resp.value.blockhash
# Sign transaction
transaction.sign(from_keypair)
# Send transaction
response = client.send_raw_transaction(
transaction.serialize()
)
signature = response.value
print(f"Transaction sent: {signature}")
# Wait for confirmation
client.confirm_transaction(signature)
print(f"Transaction confirmed: {signature}")
return signature
# Example usage
sender = Keypair.from_secret_key(b"your_secret_key")
send_transaction(
sender,
'vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg',
0.1
)# Note: You need to construct and sign the transaction first
# This example shows the raw RPC call
curl -X POST https://nexus.fortiblox.com/rpc \
-H "X-API-Key: $FORTIBLOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "sendTransaction",
"params": [
"BASE64_ENCODED_SIGNED_TRANSACTION",
{
"encoding": "base64",
"skipPreflight": false,
"preflightCommitment": "confirmed",
"maxRetries": 3
}
]
}'Use Cases:
- SOL transfers between wallets
- Token transfers (SPL tokens)
- Program instruction execution
- Account creation and initialization
Performance Tips:
- Heavy method: 10 credits - Most expensive operation
- Use
skipPreflight: falseto catch errors before sending - Set appropriate
maxRetriesfor reliability - Always confirm transactions before showing success to users
Security Note: Never expose private keys in client-side code. Always sign transactions on the backend or use wallet adapters for browser applications.
simulateTransaction
Simulates a transaction without actually submitting it to the network.
async function simulateTransaction(transaction) {
// Ensure transaction has recent blockhash
const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
// Simulate (doesn't require signatures)
const simulation = await connection.simulateTransaction(transaction);
if (simulation.value.err) {
console.error('Simulation failed:', simulation.value.err);
console.error('Logs:', simulation.value.logs);
return { success: false, error: simulation.value.err };
}
console.log('Simulation successful');
console.log('Units consumed:', simulation.value.unitsConsumed);
console.log('Logs:', simulation.value.logs);
return {
success: true,
unitsConsumed: simulation.value.unitsConsumed,
logs: simulation.value.logs
};
}
// Simulate before sending
async function sendWithSimulation(fromKeypair, toAddress, amountSOL) {
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: new PublicKey(toAddress),
lamports: amountSOL * LAMPORTS_PER_SOL
})
);
// Set fee payer for simulation
transaction.feePayer = fromKeypair.publicKey;
// Simulate first
const simulation = await simulateTransaction(transaction);
if (!simulation.success) {
throw new Error(`Simulation failed: ${JSON.stringify(simulation.error)}`);
}
console.log('Simulation passed, sending transaction...');
// Sign and send
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.sign(fromKeypair);
const signature = await connection.sendRawTransaction(
transaction.serialize()
);
await connection.confirmTransaction({
signature,
blockhash,
lastValidBlockHeight
});
return signature;
}
// Check if transaction will succeed
async function willTransactionSucceed(transaction) {
try {
const simulation = await simulateTransaction(transaction);
return simulation.success;
} catch (error) {
console.error('Simulation error:', error);
return false;
}
}def simulate_transaction(transaction):
"""Simulate a transaction without sending"""
# Get recent blockhash
blockhash_resp = client.get_latest_blockhash()
transaction.recent_blockhash = blockhash_resp.value.blockhash
# Simulate
response = client.simulate_transaction(transaction)
if response.value.err:
print(f"Simulation failed: {response.value.err}")
print(f"Logs: {response.value.logs}")
return {"success": False, "error": response.value.err}
print("Simulation successful")
print(f"Units consumed: {response.value.units_consumed}")
print(f"Logs: {response.value.logs}")
return {
"success": True,
"units_consumed": response.value.units_consumed,
"logs": response.value.logs
}
def send_with_simulation(from_keypair, to_address, amount_sol):
"""Simulate before sending"""
to_pubkey = PublicKey(to_address)
# Create transaction
transfer_ix = transfer(
TransferParams(
from_pubkey=from_keypair.public_key,
to_pubkey=to_pubkey,
lamports=int(amount_sol * 1e9)
)
)
transaction = Transaction()
transaction.add(transfer_ix)
transaction.fee_payer = from_keypair.public_key
# Simulate first
simulation = simulate_transaction(transaction)
if not simulation["success"]:
raise Exception(f"Simulation failed: {simulation['error']}")
print("Simulation passed, sending transaction...")
# Sign and send
blockhash_resp = client.get_latest_blockhash()
transaction.recent_blockhash = blockhash_resp.value.blockhash
transaction.sign(from_keypair)
response = client.send_raw_transaction(transaction.serialize())
signature = response.value
client.confirm_transaction(signature)
return signaturecurl -X POST https://nexus.fortiblox.com/rpc \
-H "X-API-Key: $FORTIBLOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "simulateTransaction",
"params": [
"BASE64_ENCODED_TRANSACTION",
{
"encoding": "base64",
"commitment": "confirmed"
}
]
}'Use Cases:
- Testing transactions before spending fees
- Estimating compute units needed
- Debugging transaction failures
- Validating program logic
Performance Tips:
- Heavy method: 10 credits - Same as sendTransaction
- Doesn't require transaction signatures for simulation
- Use to catch errors before actual submission
- Great for development and testing
getFeeForMessage
Gets the fee the network will charge for a particular message (transaction).
async function getTransactionFee(transaction) {
// Get recent blockhash
const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
// Compile message
const message = transaction.compileMessage();
// Get fee
const feeResponse = await connection.getFeeForMessage(message);
if (feeResponse.value === null) {
throw new Error('Failed to get fee estimate');
}
const feeLamports = feeResponse.value;
const feeSOL = feeLamports / LAMPORTS_PER_SOL;
console.log(`Estimated fee: ${feeLamports} lamports (${feeSOL} SOL)`);
return { lamports: feeLamports, sol: feeSOL };
}
// Check if user can afford transaction
async function canAffordTransaction(payerPubkey, transaction) {
const balance = await connection.getBalance(payerPubkey);
const fee = await getTransactionFee(transaction);
// Calculate total cost (including transfer amount if applicable)
let totalCost = fee.lamports;
// Add transfer amount if it's a transfer instruction
transaction.instructions.forEach(ix => {
if (ix.programId.equals(SystemProgram.programId)) {
// Parse transfer amount (simplified)
totalCost += ix.data.readBigUInt64LE(4);
}
});
const canAfford = balance >= totalCost;
console.log(`Balance: ${balance} lamports`);
console.log(`Total cost: ${totalCost} lamports`);
console.log(`Can afford: ${canAfford}`);
return canAfford;
}
// Example usage
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: sender.publicKey,
toPubkey: recipient,
lamports: 0.1 * LAMPORTS_PER_SOL
})
);
getTransactionFee(transaction);def get_transaction_fee(transaction):
"""Get estimated fee for transaction"""
# Get recent blockhash
blockhash_resp = client.get_latest_blockhash()
transaction.recent_blockhash = blockhash_resp.value.blockhash
# Compile message
message = transaction.compile_message()
# Get fee
response = client.get_fee_for_message(message)
if response.value is None:
raise Exception("Failed to get fee estimate")
fee_lamports = response.value
fee_sol = fee_lamports / 1e9
print(f"Estimated fee: {fee_lamports} lamports ({fee_sol} SOL)")
return {"lamports": fee_lamports, "sol": fee_sol}
def can_afford_transaction(payer_pubkey, transaction):
"""Check if payer can afford transaction"""
balance_resp = client.get_balance(payer_pubkey)
balance = balance_resp.value
fee = get_transaction_fee(transaction)
# Simplified - add transfer amount if applicable
total_cost = fee["lamports"]
can_afford = balance >= total_cost
print(f"Balance: {balance} lamports")
print(f"Total cost: {total_cost} lamports")
print(f"Can afford: {can_afford}")
return can_afford# First, compile your transaction message
# Then get the fee estimate
curl -X POST https://nexus.fortiblox.com/rpc \
-H "X-API-Key: $FORTIBLOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getFeeForMessage",
"params": [
"BASE64_ENCODED_MESSAGE",
{
"commitment": "confirmed"
}
]
}'Use Cases:
- Estimating transaction costs before submission
- Validating sufficient balance
- Fee estimation for users
- Cost optimization
Performance Tips:
- Lightweight method (1 credit)
- Doesn't require signatures
- Fees are deterministic based on signatures and compute units
Transaction Lifecycle
Understanding the transaction lifecycle is crucial for reliable submissions:
1. Transaction States
Created → Signed → Sent → Processed → Confirmed → Finalized- Created - Transaction built with instructions
- Signed - All required signatures added
- Sent - Submitted to RPC node
- Processed - Included in a block (not confirmed yet)
- Confirmed - Supermajority of validators confirmed
- Finalized - Permanent (32+ blocks later)
2. Confirmation Strategies
// Basic confirmation
async function confirmTransaction(signature, blockhash, lastValidBlockHeight) {
const confirmation = await connection.confirmTransaction({
signature,
blockhash,
lastValidBlockHeight
});
if (confirmation.value.err) {
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`);
}
return confirmation;
}// With custom timeout
async function confirmTransactionWithTimeout(
signature,
blockhash,
lastValidBlockHeight,
timeoutMs = 60000
) {
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
const status = await connection.getSignatureStatus(signature);
if (status.value?.confirmationStatus === 'confirmed' ||
status.value?.confirmationStatus === 'finalized') {
if (status.value.err) {
throw new Error(`Transaction failed: ${JSON.stringify(status.value.err)}`);
}
return status;
}
// Wait before checking again
await new Promise(resolve => setTimeout(resolve, 1000));
}
throw new Error('Transaction confirmation timeout');
}// With automatic retries
async function sendTransactionWithRetry(
transaction,
signers,
maxRetries = 3
) {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
// Get fresh blockhash for each attempt
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.sign(...signers);
const signature = await connection.sendRawTransaction(
transaction.serialize(),
{
skipPreflight: false,
maxRetries: 0 // We handle retries manually
}
);
console.log(`Attempt ${attempt + 1}: Transaction sent ${signature}`);
// Wait for confirmation
await connection.confirmTransaction({
signature,
blockhash,
lastValidBlockHeight
});
console.log('Transaction confirmed:', signature);
return signature;
} catch (error) {
console.error(`Attempt ${attempt + 1} failed:`, error.message);
lastError = error;
if (attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
console.log(`Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw new Error(`Transaction failed after ${maxRetries} attempts: ${lastError.message}`);
}Error Handling
Common Transaction Errors
// Error: Insufficient funds
async function sendWithBalanceCheck(fromKeypair, toAddress, amountSOL) {
const balance = await connection.getBalance(fromKeypair.publicKey);
const requiredLamports = amountSOL * LAMPORTS_PER_SOL;
const estimatedFee = 5000; // 5000 lamports typical fee
if (balance < requiredLamports + estimatedFee) {
throw new Error(
`Insufficient balance. Have: ${balance / LAMPORTS_PER_SOL} SOL, ` +
`Need: ${(requiredLamports + estimatedFee) / LAMPORTS_PER_SOL} SOL`
);
}
return sendTransaction(fromKeypair, toAddress, amountSOL);
}// Error: Blockhash expired
async function sendWithFreshBlockhash(transaction, signers) {
// Get blockhash immediately before sending
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash('finalized');
transaction.recentBlockhash = blockhash;
transaction.sign(...signers);
// Send immediately
const signature = await connection.sendRawTransaction(
transaction.serialize(),
{ maxRetries: 3 }
);
// Confirm with the same blockhash reference
await connection.confirmTransaction({
signature,
blockhash,
lastValidBlockHeight
});
return signature;
}// Error: Simulation failed
async function handleSimulationError(transaction) {
try {
const simulation = await connection.simulateTransaction(transaction);
if (simulation.value.err) {
console.error('Simulation failed with error:', simulation.value.err);
console.error('Program logs:');
simulation.value.logs?.forEach(log => console.error(' ', log));
// Parse common errors
if (JSON.stringify(simulation.value.err).includes('InsufficientFundsForFee')) {
throw new Error('Insufficient funds to pay transaction fee');
}
if (JSON.stringify(simulation.value.err).includes('InvalidAccountData')) {
throw new Error('Invalid account data - check account initialization');
}
throw new Error(`Simulation failed: ${JSON.stringify(simulation.value.err)}`);
}
return simulation;
} catch (error) {
console.error('Simulation error:', error);
throw error;
}
}// Error: Network/RPC errors
async function sendWithNetworkRetry(transaction, signers) {
const maxAttempts = 3;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await sendTransactionWithRetry(transaction, signers, 1);
} catch (error) {
// Check if it's a network error
if (error.message.includes('fetch') ||
error.message.includes('network') ||
error.message.includes('timeout')) {
console.error(`Network error on attempt ${attempt + 1}:`, error.message);
if (attempt < maxAttempts - 1) {
await new Promise(resolve => setTimeout(resolve, 2000));
continue;
}
}
throw error;
}
}
}Best Practices
1. Always Simulate First (Development)
async function safeSendTransaction(transaction, signers) {
// Set fee payer
transaction.feePayer = signers[0].publicKey;
// Simulate first
console.log('Simulating transaction...');
const simulation = await connection.simulateTransaction(transaction);
if (simulation.value.err) {
throw new Error(`Simulation failed: ${JSON.stringify(simulation.value.err)}`);
}
console.log('Simulation passed. Sending transaction...');
// Get fresh blockhash
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
// Sign
transaction.sign(...signers);
// Send
const signature = await connection.sendRawTransaction(
transaction.serialize()
);
// Confirm
await connection.confirmTransaction({
signature,
blockhash,
lastValidBlockHeight
});
return signature;
}2. Implement Proper Retry Logic
class TransactionSender {
constructor(connection, options = {}) {
this.connection = connection;
this.maxRetries = options.maxRetries || 3;
this.retryDelay = options.retryDelay || 1000;
this.skipPreflight = options.skipPreflight || false;
}
async send(transaction, signers) {
let lastError;
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
try {
// Get fresh blockhash
const { blockhash, lastValidBlockHeight } =
await this.connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.sign(...signers);
// Send with preflight checks
const signature = await this.connection.sendRawTransaction(
transaction.serialize(),
{
skipPreflight: this.skipPreflight,
preflightCommitment: 'confirmed',
maxRetries: 0
}
);
console.log(`Sent transaction: ${signature}`);
// Confirm
const confirmation = await this.connection.confirmTransaction({
signature,
blockhash,
lastValidBlockHeight
});
if (confirmation.value.err) {
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`);
}
return signature;
} catch (error) {
lastError = error;
console.error(`Attempt ${attempt + 1} failed:`, error.message);
if (attempt < this.maxRetries - 1) {
const delay = this.retryDelay * Math.pow(2, attempt);
console.log(`Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw new Error(
`Transaction failed after ${this.maxRetries} attempts: ${lastError.message}`
);
}
}
// Usage
const sender = new TransactionSender(connection, {
maxRetries: 3,
retryDelay: 1000,
skipPreflight: false
});
const signature = await sender.send(transaction, [signer]);3. Monitor Transaction Status
async function monitorTransaction(signature) {
const maxChecks = 60; // Check for up to 60 seconds
let checks = 0;
console.log(`Monitoring transaction: ${signature}`);
while (checks < maxChecks) {
const status = await connection.getSignatureStatus(signature);
if (status.value === null) {
console.log(`Check ${checks + 1}: Not found yet`);
} else {
const confirmation = status.value.confirmationStatus;
const hasError = status.value.err !== null;
console.log(`Check ${checks + 1}: ${confirmation}${hasError ? ' (ERROR)' : ''}`);
if (confirmation === 'finalized') {
if (hasError) {
throw new Error(`Transaction failed: ${JSON.stringify(status.value.err)}`);
}
console.log('Transaction finalized successfully');
return status;
}
}
checks++;
await new Promise(resolve => setTimeout(resolve, 1000));
}
throw new Error('Transaction monitoring timeout');
}4. Use Transaction Receipts
async function getTransactionReceipt(signature) {
// Wait for transaction to be finalized
await connection.confirmTransaction(signature, 'finalized');
// Get full transaction details
const transaction = await connection.getTransaction(signature, {
maxSupportedTransactionVersion: 0
});
if (!transaction) {
throw new Error('Transaction not found');
}
// Extract receipt information
const receipt = {
signature,
slot: transaction.slot,
blockTime: new Date(transaction.blockTime * 1000),
fee: transaction.meta.fee / LAMPORTS_PER_SOL,
success: transaction.meta.err === null,
preBalances: transaction.meta.preBalances.map(b => b / LAMPORTS_PER_SOL),
postBalances: transaction.meta.postBalances.map(b => b / LAMPORTS_PER_SOL),
logs: transaction.meta.logMessages
};
console.log('Transaction Receipt:', JSON.stringify(receipt, null, 2));
return receipt;
}Real-World Use Cases
Payment System with Confirmation
class PaymentProcessor {
constructor(connection) {
this.connection = connection;
}
async processPayment(senderKeypair, recipientAddress, amountSOL) {
const recipient = new PublicKey(recipientAddress);
// 1. Validate balance
const balance = await this.connection.getBalance(senderKeypair.publicKey);
const requiredLamports = amountSOL * LAMPORTS_PER_SOL + 5000; // + fee
if (balance < requiredLamports) {
throw new Error('Insufficient balance');
}
// 2. Create transaction
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: senderKeypair.publicKey,
toPubkey: recipient,
lamports: amountSOL * LAMPORTS_PER_SOL
})
);
// 3. Simulate
transaction.feePayer = senderKeypair.publicKey;
const { blockhash } = await this.connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
const simulation = await this.connection.simulateTransaction(transaction);
if (simulation.value.err) {
throw new Error(`Simulation failed: ${JSON.stringify(simulation.value.err)}`);
}
// 4. Send with retry
const sender = new TransactionSender(this.connection, { maxRetries: 3 });
const signature = await sender.send(transaction, [senderKeypair]);
// 5. Get receipt
const receipt = await getTransactionReceipt(signature);
// 6. Return payment confirmation
return {
success: true,
signature,
amount: amountSOL,
fee: receipt.fee,
timestamp: receipt.blockTime,
recipient: recipientAddress
};
}
}
// Usage
const processor = new PaymentProcessor(connection);
const result = await processor.processPayment(
senderKeypair,
'vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg',
0.1
);
console.log('Payment successful:', result);Batch Transaction Processor
async function processBatchPayments(senderKeypair, payments) {
// payments: [{ recipient: string, amount: number }, ...]
const results = [];
for (const payment of payments) {
try {
console.log(`Processing payment to ${payment.recipient}...`);
const signature = await sendTransaction(
senderKeypair,
payment.recipient,
payment.amount
);
results.push({
recipient: payment.recipient,
amount: payment.amount,
signature,
success: true
});
// Rate limiting
await new Promise(resolve => setTimeout(resolve, 500));
} catch (error) {
console.error(`Failed to send to ${payment.recipient}:`, error.message);
results.push({
recipient: payment.recipient,
amount: payment.amount,
error: error.message,
success: false
});
}
}
// Summary
const successful = results.filter(r => r.success).length;
const failed = results.filter(r => !r.success).length;
console.log(`\nBatch complete: ${successful} successful, ${failed} failed`);
return results;
}Performance Optimization
Use Priority Fees (When Available)
async function sendWithPriorityFee(transaction, signers, priorityFee) {
// Add compute budget instruction for priority fee
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
units: 200000
});
const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: priorityFee
});
// Add priority instructions at the beginning
transaction.instructions = [
modifyComputeUnits,
addPriorityFee,
...transaction.instructions
];
return sendTransaction(transaction, signers);
}Optimize Compute Units
async function optimizeComputeUnits(transaction) {
// Simulate to get actual compute units used
const simulation = await connection.simulateTransaction(transaction);
if (simulation.value.err) {
throw new Error('Simulation failed');
}
const unitsUsed = simulation.value.unitsConsumed;
console.log(`Compute units used: ${unitsUsed}`);
// Add 10% buffer
const unitsToRequest = Math.ceil(unitsUsed * 1.1);
// Add compute budget instruction
transaction.instructions.unshift(
ComputeBudgetProgram.setComputeUnitLimit({
units: unitsToRequest
})
);
return transaction;
}