Sending a Legacy Transaction

Once a web application is connected to Jelli, it can prompt the user for permission to send transactions on their behalf. Legacy transactions are the traditional Solana transaction format with a maximum size of 1,232 bytes and support for up to 35 accounts per transaction.

In order to send a transaction, a web application must:

  1. Create an unsigned transaction

  2. Have the transaction be signed and submitted to the network by the user's Jelli wallet

  3. Optionally await network confirmation using a Solana JSON RPC connection

For more information about the nature of Solana transactions, refer to the solana-web3.js documentation and the Solana Cookbook.


πŸ“‹ What You'll Learn

  1. Signing and sending legacy transactions

  2. Using the request() method for flexibility

  3. Handling multiple transactions

  4. Alternative signing methods

  5. Error handling and debugging


The easiest and most recommended way to send a transaction is by using the signAndSendTransaction method on the provider. This method handles both signing and network submission in a single call.

signAndSendTransaction() with Jelli

import { Connection, Transaction, SystemProgram, PublicKey } from '@solana/web3.js';

const provider = window.jelli.solana; // Jelli wallet  
const connection = new Connection('https://api.devnet.solana.com');

// Connect to wallet
const { publicKey } = await provider.connect();

// Create a simple transfer transaction
const transaction = new Transaction().add(
  SystemProgram.transfer({
    fromPubkey: publicKey,
    toPubkey: new PublicKey('DL8vmGYQAZfy4mC84x1AjCet9tvNzpJrG1E9qVuT1ePY'),
    lamports: 1000000, // 0.001 SOL
  })
);

// Get recent blockhash
transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
transaction.feePayer = publicKey;

// Sign and send via Jelli
const { signature } = await provider.signAndSendTransaction(transaction);

// Wait for confirmation
await connection.getSignatureStatus(signature);
console.log('🎯 Transaction confirmed:', signature);

Enhanced SendOptions Support

const options = {
  skipPreflight: false,
  preflightCommitment: 'processed',
  maxRetries: 3,
  minContextSlot: 1000
};

const { signature } = await provider.signAndSendTransaction(transaction, options);

πŸ” Jelli Enhancement: Automatic detection and logging of transaction type (Legacy vs Versioned) with detailed timing information.


πŸ”„ Using the request() Method

Jelli supports both the modern object format and legacy string format for the request() method, providing maximum compatibility.

Modern Object Format

const provider = window.jelli.solana;
const connection = new Connection('https://api.devnet.solana.com');

// Create your transaction
const transaction = new Transaction();
// ... add instructions ...

// Modern format
const { signature } = await provider.request({
  method: "signAndSendTransaction",
  params: {
    transaction: transaction,
    options: {
      skipPreflight: false,
      preflightCommitment: 'processed'
    }
  }
});

await connection.getSignatureStatus(signature);

Legacy String Format (Phantom Compatible)

import bs58 from 'bs58';

const provider = window.jelli.solana;
const connection = new Connection('https://api.devnet.solana.com');

// Create transaction
const transaction = new Transaction();
// ... add instructions and set recent blockhash ...

// Legacy format (exactly like Phantom)
const { signature } = await provider.request(
  "signAndSendTransaction",
  {
    message: bs58.encode(transaction.serializeMessage())
  }
);

await connection.getSignatureStatus(signature);

πŸ’‘ Jelli Advantage: Supports both formats seamlessly, so existing Phantom code works without changes.


πŸ“¦ Sign and Send Multiple Transactions

Jelli supports batch transaction processing through the signAndSendAllTransactions method, which is more efficient and safer than individual transaction calls.

signAndSendAllTransactions() with Jelli

const provider = window.jelli.solana;
const connection = new Connection('https://api.devnet.solana.com');

// Create multiple transactions
const transactions = [
  new Transaction().add(/* first instruction */),
  new Transaction().add(/* second instruction */),
  new Transaction().add(/* third instruction */)
];

// Set blockhash and fee payer for all transactions
const { blockhash } = await connection.getLatestBlockhash();
transactions.forEach(tx => {
  tx.recentBlockhash = blockhash;
  tx.feePayer = publicKey;
});

// Sign and send all transactions
const { signatures, publicKey: signerKey } = await provider.signAndSendAllTransactions(
  transactions,
  {
    skipPreflight: false,
    preflightCommitment: 'processed'
  }
);

// Wait for all confirmations
await connection.getSignatureStatuses(signatures);
console.log('🎯 All transactions confirmed:', signatures);

πŸš€ Jelli Enhancement: Progress tracking and detailed logging for each transaction in the batch.


The following methods are supported for legacy compatibility but are not recommended over signAndSendTransaction. It's safer for users, and simpler for developers, for Jelli to submit transactions immediately after signing.

πŸ“’ Important: These methods are not supported in the Wallet Standard implementation and may be removed in a future release. They are only available via the window.jelli.solana object.

Sign a Transaction (Without Sending)

const provider = window.jelli.solana;
const connection = new Connection('https://api.devnet.solana.com');

// Create transaction
const transaction = new Transaction();
// ... add instructions ...

// Sign without sending (NOT RECOMMENDED)
const signedTransaction = await provider.signTransaction(transaction);

// Manually send the transaction (your responsibility)
const signature = await connection.sendRawTransaction(signedTransaction.serialize());
console.log('πŸ“ Transaction signature:', signature);

Using request() for Signing Only

import bs58 from 'bs58';

const provider = window.jelli.solana;
const connection = new Connection('https://api.devnet.solana.com');

// Create transaction
const transaction = new Transaction();
// ... configure transaction ...

// Sign via request method
const signedTransaction = await provider.request({
  method: "signTransaction",
  params: {
    message: bs58.encode(transaction.serializeMessage())
  }
});

// Manually broadcast
const signature = await connection.sendRawTransaction(signedTransaction.serialize());

Sign Multiple Transactions (Legacy)

// Legacy batch signing (NOT RECOMMENDED for new integrations)
const signedTransactions = await provider.signAllTransactions(transactions);

// Manual broadcasting required
for (const signedTx of signedTransactions) {
  const sig = await connection.sendRawTransaction(signedTx.serialize());
  console.log('Transaction sent:', sig);
}

πŸ›‘οΈ Error Handling

Jelli uses exactly the same error codes as Phantom but with enhanced error messages and Jelli branding.

Complete Error Handling Example

try {
  const { signature } = await provider.signAndSendTransaction(transaction);
  console.log('βœ… Transaction successful:', signature);
  
} catch (error) {
  switch (error.code) {
    case 4001:
      // User rejected the transaction
      console.log('❌ User rejected:', error.message);
      // "The user rejected the request through Jelli."
      break;
      
    case 4100:
      // Wallet not connected
      console.log('❌ Unauthorized:', error.message);
      // "The requested method and/or account has not been authorized by the user."
      break;
      
    case -32003:
      // Invalid transaction format
      console.log('❌ Transaction rejected:', error.message);
      // "Jelli does not recognize a valid transaction."
      break;
      
    case -32000:
      // Invalid parameters
      console.log('❌ Invalid input:', error.message);
      // "Missing or invalid parameters."
      break;
      
    case -32002:
      // Resource not available (transaction queue full)
      console.log('❌ Resource unavailable:', error.message);
      // "This error occurs when a dapp attempts to submit a new transaction while Jelli's approval dialog is already open..."
      break;
      
    case -32603:
      // Internal error
      console.log('❌ Internal error:', error.message);
      // "Something went wrong within Jelli."
      break;
      
    default:
      console.error('❌ Unknown error:', error.code, error.message);
  }
}

Jelli Enhanced Error Details

try {
  await provider.signAndSendTransaction(transaction);
} catch (error) {
  // Jelli provides additional debugging information
  console.log('Error details:', {
    code: error.code,
    message: error.message,
    timestamp: error.timestamp,
    transactionType: error.transactionType, // 'legacy' or 'versioned'
    category: error.category,
    metadata: error.metadata
  });
}

πŸ” Jelli Exclusive Debugging Features

Enhanced Transaction Logging

// Enable detailed logging (Jelli-specific)
window.jelliDebug = true;

try {
  const { signature } = await provider.signAndSendTransaction(transaction);
  
  // Jelli automatically logs:
  // πŸ” Transaction type detected: Legacy Transaction
  // πŸ“¦ Serialized size: 245 bytes
  // ⏱️ Signing time: 1.2s
  // πŸ“‘ Network submission: 0.8s
  // βœ… Total time: 2.0s
  
} catch (error) {
  // Enhanced error context
  console.log('πŸ” Jelli Debug Info:', {
    transactionSize: error.metadata?.transactionSize,
    networkLatency: error.metadata?.networkLatency,
    userActionTime: error.metadata?.userActionTime
  });
}

Performance Monitoring

// Jelli tracks performance metrics
const startTime = performance.now();
const { signature } = await provider.signAndSendTransaction(transaction);
const endTime = performance.now();

console.log('πŸ“Š Performance Metrics:');
console.log(`  Total time: ${endTime - startTime}ms`);
console.log(`  Transaction size: ${transaction.serialize().length} bytes`);
console.log(`  Efficiency: ${35 - transaction.instructions.length} account slots remaining`);

πŸš€ Complete Working Example

async function sendLegacyTransactionWithJelli() {
  try {
    // 1. Get provider and connection
    const provider = window.jelli.solana;
    const connection = new Connection('https://api.devnet.solana.com');
    
    // 2. Connect wallet
    const { publicKey } = await provider.connect();
    console.log('πŸ”— Connected to Jelli:', publicKey.toBase58());
    
    // 3. Create legacy transaction
    const transaction = new Transaction().add(
      SystemProgram.transfer({
        fromPubkey: publicKey,
        toPubkey: publicKey, // Self-transfer for demo
        lamports: 1000000 // 0.001 SOL
      })
    );
    
    // 4. Set transaction metadata
    const { blockhash } = await connection.getLatestBlockhash();
    transaction.recentBlockhash = blockhash;
    transaction.feePayer = publicKey;
    
    // 5. Sign and send via Jelli
    const { signature } = await provider.signAndSendTransaction(transaction, {
      skipPreflight: false,
      preflightCommitment: 'processed'
    });
    
    console.log('🎯 Legacy transaction sent:', signature);
    
    // 6. Wait for confirmation
    const confirmation = await connection.confirmTransaction(signature);
    console.log('βœ… Transaction confirmed:', confirmation.value);
    
    return signature;
    
  } catch (error) {
    console.error('❌ Transaction failed:', error.code, error.message);
    throw error;
  }
}

// Run the example
sendLegacyTransactionWithJelli()
  .then(sig => console.log('πŸŽ‰ Success:', sig))
  .catch(err => console.error('πŸ’₯ Failed:', err));

πŸ“š References

Last updated