typescript
Send a transaction using TypeScript with full type safety.
Full Example
bash
npm install @solana/web3.js@1 bs58typescript
import {
Connection,
Keypair,
PublicKey,
SystemProgram,
Transaction,
TransactionInstruction,
ComputeBudgetProgram,
} from "@solana/web3.js";
import bs58 from "bs58";
// Your Solana RPC: blockhash and confirmations
const RPC_URL = "https://api.mainnet-beta.solana.com";
// BlockSprint: sending only
const ENDPOINT = "https://fra.blocksprint.io/api/v1/YOUR_API_KEY";
// BlockSprint tip addresses
const TIP_ADDRESSES: string[] = [
"Sp1x2AqpQckPLaWnWCJUNg8k6qQexfaEWcSRKf5JcDV",
"Sp4JHSh9cksfzXbgK7Pq2ovtn8LirLQydaJKTsiNT77",
"Sp1xMS2cbw83SZDNr4AGqkBYYLjb3LvVnmDSrTMaHkr",
"SpagSJmnh8E9cGT5Y431xPPaS2c1xLREGGCWN9yDeUf",
"SpWrza9E63MQuHeGnnfzmtLVCs3pBdjyKPXUABPo9nq",
"SpXyoqMHJEhsNQ9fHh3DTGGzrGroZ5zfDtJwFvN4kXi",
"Spv2g3L837bVeHwkyb18LhA5P1bvJbkvaZbxcZXbwC2",
"SpDUe5MyFGG97SmdBQMWNPbx2Et9bZt2TCbZm3TujgX",
"SpKZ28tFzQ13kVjHhsyDEiQfkPGjA7Wmk185KcEDyuh",
"SpxPqegfoR3QkBN7guRUiU6QfhtTW1CnKSTSSapumqm",
];
function getRandomTipAddress(): PublicKey {
const index = Math.floor(Math.random() * TIP_ADDRESSES.length);
return new PublicKey(TIP_ADDRESSES[index]);
}
interface SendOptions {
priorityFee?: number; // micro-lamports per compute unit
tipAmount?: number; // lamports
}
async function sendTransaction(
rpc: Connection,
blocksprint: Connection,
wallet: Keypair,
instructions: TransactionInstruction[],
options: SendOptions = {}
): Promise<string> {
// Tip: 0.001 SOL minimum
const { priorityFee = 1_000_000, tipAmount = 1_000_000 } = options;
const transaction = new Transaction();
// Priority fee
transaction.add(
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: priorityFee,
})
);
// Your instructions
instructions.forEach((ix) => transaction.add(ix));
// Tip instruction
transaction.add(
SystemProgram.transfer({
fromPubkey: wallet.publicKey,
toPubkey: getRandomTipAddress(),
lamports: tipAmount,
})
);
// Get blockhash from your RPC
const { blockhash } = await rpc.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.feePayer = wallet.publicKey;
// Sign
transaction.sign(wallet);
// Send through BlockSprint
const signature = await blocksprint.sendRawTransaction(
transaction.serialize(),
{
skipPreflight: true,
maxRetries: 0,
}
);
return signature;
}
// Usage
async function main() {
const rpc = new Connection(RPC_URL, "confirmed");
const blocksprint = new Connection(ENDPOINT);
// Your funded wallet
const wallet = Keypair.fromSecretKey(bs58.decode(process.env.PAYER_SECRET_BASE58!));
const transferIx = SystemProgram.transfer({
fromPubkey: wallet.publicKey,
toPubkey: new PublicKey(process.env.RECIPIENT!),
lamports: 1_000_000,
});
const signature = await sendTransaction(rpc, blocksprint, wallet, [transferIx], {
priorityFee: 1_000_000,
tipAmount: 1_000_000,
});
// "Accepted", not "landed": check it with rpc.getSignatureStatuses([signature])
console.log("Signature:", signature);
}
main().catch(console.error);Type-Safe Fetch Wrapper
typescript
interface JsonRpcRequest {
jsonrpc: "2.0";
id: number;
method: string;
params: unknown[];
}
interface JsonRpcResponse<T> {
jsonrpc: "2.0";
id: number | null;
result?: T;
error?: {
code: number;
message: string;
};
}
async function rpcCall<T>(
endpoint: string,
method: string,
params: unknown[]
): Promise<T> {
const request: JsonRpcRequest = {
jsonrpc: "2.0",
id: 1,
method,
params,
};
const response = await fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
});
// Errors come back as JSON-RPC errors with an HTTP 4xx/5xx status
const data = (await response.json()) as JsonRpcResponse<T>;
if (data.error) {
throw new Error(`RPC Error ${data.error.code}: ${data.error.message}`);
}
return data.result as T;
}
// Usage
const signature = await rpcCall<string>(
ENDPOINT,
"sendTransaction",
[base64Transaction, { encoding: "base64", skipPreflight: true, maxRetries: 0 }]
);INFO
Transaction V1: @solana/web3.js 1.x cannot build V1 transactions. Use @solana/kit 8.0.0 or later: create the message with createTransactionMessage({ version: 1 }), set the compute-unit limit and the loaded-accounts data size limit explicitly (in V1 an unset limit is 0), and send getBase64EncodedWireTransaction(signedTransaction) with the wrapper above.
