The Server SDK is currently experimental and not ready for production use.

Signing and Sending Transactions

This guide covers how to sign and send transactions using the Phantom Server SDK across different blockchain networks.

Overview

The SDK provides a simple, unified interface that handles both signing AND sending transactions:
const signedResult = await sdk.signAndSendTransaction({
  walletId,         // The wallet to sign with
  transaction,     // Web3.js transaction object, or evm ones
  networkId         // Target network
});


// The SDK automatically submits the transaction to the blockchain!
// You get back the signed transaction to extract the signature
The signAndSendTransaction method does two things automatically:
  1. Signs the transaction with the wallet’s private key
  2. Submits the signed transaction to the blockchain network
You don’t need to manually submit the transaction - the SDK handles this for you!

Basic Solana Transfer

Here’s a complete example of sending SOL from one address to another:
import { ServerSDK, NetworkId } from '@phantom/server-sdk';
import { 
  Connection, 
  Transaction, 
  SystemProgram, 
  PublicKey,
  LAMPORTS_PER_SOL 
} from '@solana/web3.js';
import bs58 from 'bs58';

async function sendSOL(
  sdk: ServerSDK,
  walletId: string,
  fromAddress: string,
  toAddress: string,
  amountSOL: number
) {
  // Connect to Solana network
  const connection = new Connection('https://api.mainnet-beta.solana.com');
  
  // Create transaction
  const transaction = new Transaction().add(
    SystemProgram.transfer({
      fromPubkey: new PublicKey(fromAddress),
      toPubkey: new PublicKey(toAddress),
      lamports: amountSOL * LAMPORTS_PER_SOL
    })
  );
  
  // Get recent blockhash
  const { blockhash } = await connection.getLatestBlockhash();
  transaction.recentBlockhash = blockhash;
  transaction.feePayer = new PublicKey(fromAddress);
  

  // Sign and send - SDK handles both!
  const signedResult = await sdk.signAndSendTransaction({
    walletId,
    transaction,
    networkId: NetworkId.SOLANA_MAINNET
  });

  console.log('✅ Transaction signed and sent!');
  
  // Extract signature from the returned signed transaction
  const signedTx = Transaction.from(
    Buffer.from(signedResult.rawTransaction, 'base64url')
  );
  
  let signature: string;
  if (signedTx.signature) {
    signature = bs58.encode(signedTx.signature);
  } else if (signedTx.signatures?.[0]?.signature) {
    signature = bs58.encode(signedTx.signatures[0].signature);
  } else {
    throw new Error('Failed to extract signature');
  }
  
  console.log(`Transaction signature: ${signature}`);
  
  // Wait for confirmation
  const confirmation = await connection.confirmTransaction(signature);
  
  return {
    signature,
    confirmed: !confirmation.value.err
  };
}

Complete Example with Priority Fees

Based on the SDK demo, here’s how to send a transaction with priority fees:
import { ComputeBudgetProgram } from '@solana/web3.js';

async function sendWithPriorityFee(
  sdk: ServerSDK,
  walletId: string,
  fromAddress: string,
  toAddress: string,
  amountSOL: number
) {
  const connection = new Connection('https://api.devnet.solana.com');
  const transaction = new Transaction();
  
  // Add priority fee instruction first
  const priorityFee = 1000; // micro-lamports per compute unit
  transaction.add(
    ComputeBudgetProgram.setComputeUnitPrice({
      microLamports: priorityFee
    })
  );
  
  // Add transfer instruction
  transaction.add(
    SystemProgram.transfer({
      fromPubkey: new PublicKey(fromAddress),
      toPubkey: new PublicKey(toAddress),
      lamports: Math.floor(amountSOL * LAMPORTS_PER_SOL)
    })
  );
  
  // Prepare transaction
  const { blockhash } = await connection.getLatestBlockhash();
  transaction.recentBlockhash = blockhash;
  transaction.feePayer = new PublicKey(fromAddress);
  

  // Sign and send (SDK does both!)
  const signedResult = await sdk.signAndSendTransaction({
    walletId,
    transaction,
    networkId: NetworkId.SOLANA_DEVNET
  });

  // Extract signature
  const signedTx = Transaction.from(
    Buffer.from(signedResult.rawTransaction, 'base64url')
  );
  const signature = bs58.encode(signedTx.signatures[0].signature);
  
  console.log(`Transaction sent with signature: ${signature}`);
  
  // Wait for confirmation
  const startTime = Date.now();
  let confirmed = false;
  let attempts = 0;
  
  while (!confirmed && attempts < 30) {
    const status = await connection.getSignatureStatus(signature);
    
    if (status?.value) {
      if (status.value.err) {
        throw new Error(`Transaction failed: ${JSON.stringify(status.value.err)}`);
      }
      
      if (status.value.confirmationStatus === 'confirmed' || 
          status.value.confirmationStatus === 'finalized') {
        confirmed = true;
        const elapsed = (Date.now() - startTime) / 1000;
        console.log(`✅ Confirmed in ${elapsed.toFixed(1)} seconds`);
      }
    }
    
    if (!confirmed) {
      attempts++;
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  }
  
  return { signature, confirmed };
}

Key Points to Remember

  1. The SDK handles both signing AND sending - You don’t need to manually submit transactions
  2. Always use fresh blockhashes - Get a new blockhash right before creating the transaction
  3. Extract signatures from the result - The SDK returns the signed transaction, not the signature directly
  4. Handle confirmations separately - The SDK sends the transaction but doesn’t wait for confirmation
  5. Use proper error handling - Network issues and blockchain errors can occur

Next Steps

Disclaimers

The Server SDK is a beta version, and Phantom will not be liable for any losses or damages suffered by you or your end users. Any suggestions, enhancement requests, recommendations, or other feedback provided by you regarding the Server SDK will be the exclusive property of Phantom. By using this beta version and providing feedback, you agree to assign any rights in that feedback to Phantom.