Submit Single Transaction
Submit individual transactions to NextBlock using JavaScript/TypeScript with proper tipping and error handling.
import {
Connection,
Keypair,
PublicKey,
SystemProgram,
Transaction,
sendAndConfirmTransaction
} from '@solana/web3.js';
// NextBlock tip wallets for load balancing
const NEXTBLOCK_TIP_WALLETS = [
'NEXTbLoCkB51HpLBLojQfpyVAMorm3zzKg7w9NFdqid',
'nextBLoCkPMgmG8ZgJtABeScP35qLa2AMCNKntAP7Xc',
'NextbLoCkVtMGcV47JzewQdvBpLqT9TxQFozQkN98pE',
'NexTbLoCkWykbLuB1NkjXgFWkX9oAtcoagQegygXXA2',
'NeXTBLoCKs9F1y5PJS9CKrFNNLU1keHW71rfh7KgA1X',
'NexTBLockJYZ7QD7p2byrUa6df8ndV2WSd8GkbWqfbb',
'neXtBLock1LeC67jYd1QdAa32kbVeubsfPNTJC1V5At',
'nEXTBLockYgngeRmRrjDV31mGSekVPqZoMGhQEZtPVG',
];
// Submission options interface
interface SubmissionOptions {
skipPreflight?: boolean;
frontRunningProtection?: boolean;
revertOnFail?: boolean;
disableRetries?: boolean;
snipeTransaction?: boolean;
}
// Get random tip wallet for load balancing
function getRandomNextblockTipWallet(): PublicKey {
const randomIndex = Math.floor(Math.random() * NEXTBLOCK_TIP_WALLETS.length);
return new PublicKey(NEXTBLOCK_TIP_WALLETS[randomIndex]);
}
// Build transaction with tip
async function buildTransactionWithTip(
connection: Connection,
payer: Keypair,
tipAmount: number,
instructions: any[]
): Promise<Transaction> {
// Get recent blockhash
const { blockhash } = await connection.getLatestBlockhash('finalized');
// Create tip instruction (should be first)
const tipWallet = getRandomNextblockTipWallet();
const tipInstruction = SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: tipWallet,
lamports: tipAmount,
});
// Create transaction with tip and user instructions
const transaction = new Transaction({
recentBlockhash: blockhash,
feePayer: payer.publicKey,
});
transaction.add(tipInstruction);
instructions.forEach(instruction => transaction.add(instruction));
// Sign transaction
transaction.sign(payer);
return transaction;
}
// Submit single transaction to NextBlock
async function submitSingleTransaction(
// nextblockClient: any, // Your generated gRPC client
connection: Connection,
signer: Keypair,
recipient: PublicKey,
transferAmount: number,
tipAmount: number,
options: SubmissionOptions = {}
): Promise<string> {
// Default options
const opts: SubmissionOptions = {
skipPreflight: true,
frontRunningProtection: false,
revertOnFail: false,
disableRetries: false,
snipeTransaction: false,
...options,
};
// Create transfer instruction
const transferInstruction = SystemProgram.transfer({
fromPubkey: signer.publicKey,
toPubkey: recipient,
lamports: transferAmount,
});
// Build transaction with tip
const transaction = await buildTransactionWithTip(
connection,
signer,
tipAmount,
[transferInstruction]
);
// Convert to base64 for submission
const serializedTx = transaction.serialize();
const base64Tx = serializedTx.toString('base64');
// Submit to NextBlock
/* Uncomment when you have the generated gRPC client
const request = {
transaction: { content: base64Tx },
skipPreFlight: opts.skipPreflight,
snipeTransaction: opts.snipeTransaction,
frontRunningProtection: opts.frontRunningProtection,
disableRetries: opts.disableRetries,
revertOnFail: opts.revertOnFail,
};
const response = await nextblockClient.postSubmitV2(request);
console.log('Transaction submitted successfully!');
console.log(`Signature: ${response.signature}`);
console.log(`UUID: ${response.uuid}`);
return response.signature;
*/
// Mock response for demonstration
const signature = transaction.signature?.toString() || 'mock-signature';
console.log(`Transaction built successfully: ${signature}`);
return signature;
}
// Submit with priority-based tip calculation
async function submitWithOptimalTip(
// nextblockClient: any,
connection: Connection,
signer: Keypair,
recipient: PublicKey,
transferAmount: number,
priorityLevel: 'conservative' | 'normal' | 'aggressive' | 'priority' = 'normal'
): Promise<string> {
// Tip amounts based on priority level
// Get tip amounts from current tip floor data
// Higher tips = higher priority
// Use tip floor streaming API to get current optimal tips
const tipAmount = await getOptimalTipFromTipFloor(priorityLevel);
return await submitSingleTransaction(
// nextblockClient,
connection,
signer,
recipient,
transferAmount,
tipAmount
);
}
// Transaction builder class for complex transactions
class TransactionBuilder {
private payer: Keypair;
private instructions: any[] = [];
private tipAmount: number = 1_000_000; // Default tip
constructor(payer: Keypair) {
this.payer = payer;
}
addInstruction(instruction: any): TransactionBuilder {
this.instructions.push(instruction);
return this;
}
setTipAmount(amount: number): TransactionBuilder {
this.tipAmount = amount;
return this;
}
async buildAndSubmit(
// nextblockClient: any,
connection: Connection,
options?: SubmissionOptions
): Promise<string> {
if (this.instructions.length === 0) {
throw new Error('No instructions added to transaction');
}
const transaction = await buildTransactionWithTip(
connection,
this.payer,
this.tipAmount,
this.instructions
);
// Convert and submit (similar to submitSingleTransaction)
const serializedTx = transaction.serialize();
const base64Tx = serializedTx.toString('base64');
const signature = transaction.signature?.toString() || 'mock-signature';
console.log(`Custom transaction submitted: ${signature}`);
return signature;
}
}
// Batch multiple single transactions (concurrent submission)
async function submitMultipleSingleTransactions(
// nextblockClient: any,
connection: Connection,
signer: Keypair,
transactionsData: Array<{
recipient: PublicKey;
transferAmount: number;
tipAmount: number;
}>
): Promise<string[]> {
// Create promises for all transactions
const transactionPromises = transactionsData.map(async ({ recipient, transferAmount, tipAmount }) => {
try {
return await submitSingleTransaction(
// nextblockClient,
connection,
signer,
recipient,
transferAmount,
tipAmount
);
} catch (error) {
console.error(`Transaction failed:`, error);
return null;
}
});
// Execute all transactions concurrently
const results = await Promise.allSettled(transactionPromises);
// Process results
const successfulSignatures: string[] = [];
results.forEach((result, index) => {
if (result.status === 'fulfilled' && result.value) {
successfulSignatures.push(result.value);
console.log(`Transaction ${index + 1} successful: ${result.value}`);
} else {
console.error(`Transaction ${index + 1} failed:`, result);
}
});
return successfulSignatures;
}
// Performance monitoring
class TransactionMetrics {
private submissions: number = 0;
private successes: number = 0;
private failures: number = 0;
private totalTime: number = 0;
private startTime?: number;
startSubmission(): void {
this.submissions++;
this.startTime = Date.now();
}
recordSuccess(): void {
if (this.startTime) {
this.totalTime += Date.now() - this.startTime;
this.successes++;
this.startTime = undefined;
}
}
recordFailure(): void {
if (this.startTime) {
this.totalTime += Date.now() - this.startTime;
this.failures++;
this.startTime = undefined;
}
}
getStats() {
return {
totalSubmissions: this.submissions,
successes: this.successes,
failures: this.failures,
successRate: (this.successes / Math.max(this.submissions, 1)) * 100,
averageTime: this.totalTime / Math.max(this.successes, 1),
};
}
}
// Submit with monitoring
async function submitWithMonitoring(
// nextblockClient: any,
connection: Connection,
signer: Keypair,
recipient: PublicKey,
transferAmount: number,
tipAmount: number,
metrics: TransactionMetrics
): Promise<string | null> {
metrics.startSubmission();
try {
const signature = await submitSingleTransaction(
// nextblockClient,
connection,
signer,
recipient,
transferAmount,
tipAmount
);
metrics.recordSuccess();
return signature;
} catch (error) {
metrics.recordFailure();
console.error('Transaction failed:', error);
return null;
}
}Usage Examples
Best Practices
Always include tips: NextBlock prioritizes transactions with appropriate tips
Use random tip wallets: Distribute load across multiple tip addresses
Monitor tip floors: Adjust tips based on current network conditions
Handle errors gracefully: Implement retry logic with exponential backoff
Validate inputs: Always validate public keys and amounts before submission
Use TypeScript: Leverage type safety for better development experience
Monitor performance: Track success rates and response times
Choose appropriate RPC endpoints: Use reliable RPC providers for blockhash retrieval
Last updated