Skip to content

typescript ​

Send a transaction using TypeScript with full type safety.


Full Example ​

bash
npm install @solana/web3.js@1 bs58
typescript
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.