Skip to main content
This guide covers how to create, sign, and send sponsored transactions using the Swig Paymaster SDK.

Available Methods

The paymaster client provides several methods for working with transactions:
MethodClassicKitDescription
Create legacy txcreateLegacyTransaction()N/ALegacy transaction (no lookup tables)
Create versioned txcreateTransaction()createTransaction()Versioned v0 transaction
Sign onlysign()sign()Sign without sending
Sign + validateN/AfullySign()Sign and assert fully signed
Sign + sendsignAndSend()signAndSend()Sign and submit to network

Creating Transactions

Legacy Transactions (Classic Only)

Legacy transactions are simpler but don’t support address lookup tables:
import { Keypair, PublicKey, SystemProgram } from '@solana/web3.js';
import { createPaymasterClient } from '@swig-wallet/paymaster-classic';

const paymaster = createPaymasterClient({
  apiKey: process.env.SWIG_API_KEY!,
  paymasterPubkey: process.env.PAYMASTER_PUBKEY!,
  baseUrl: 'https://api.onswig.com',
  network: 'devnet',
});

const sender = Keypair.generate();
const recipient = new PublicKey('...');

// Create a SOL transfer instruction
const transferInstruction = SystemProgram.transfer({
  fromPubkey: sender.publicKey,
  toPubkey: recipient,
  lamports: 1_000_000, // 0.001 SOL
});

// Create legacy transaction with user signing
const transaction = await paymaster.createLegacyTransaction(
  [transferInstruction],
  [sender], // Signers
);

Versioned Transactions

Versioned transactions (v0) support address lookup tables for more efficient transactions:
import { Keypair, PublicKey, SystemProgram } from '@solana/web3.js';
import { createPaymasterClient } from '@swig-wallet/paymaster-classic';

const paymaster = createPaymasterClient({
  apiKey: process.env.SWIG_API_KEY!,
  paymasterPubkey: process.env.PAYMASTER_PUBKEY!,
  baseUrl: 'https://api.onswig.com',
  network: 'devnet',
});

const sender = Keypair.generate();

const instruction = SystemProgram.transfer({
  fromPubkey: sender.publicKey,
  toPubkey: new PublicKey('...'),
  lamports: 1_000_000,
});

// Create versioned transaction (v0)
const transaction = await paymaster.createTransaction(
  [instruction],
  [sender],
  // Optional: address lookup tables
  // [lookupTableAddress]
);

Signing Transactions

The simplest approach - sign and submit in one call:
// Create transaction with user signing
const transaction = await paymaster.createLegacyTransaction(
  [instruction],
  [userKeypair],
);

// Paymaster signs and submits
const signature = await paymaster.signAndSend(transaction);
console.log(`Transaction: https://explorer.solana.com/tx/${signature}`);

Sign Only (For Inspection)

Sign without sending - useful for inspecting the transaction before submission:
const transaction = await paymaster.createLegacyTransaction(
  [instruction],
  [userKeypair],
);

// Get signed transaction without sending
const signedTx = await paymaster.sign(transaction);

// Inspect the transaction
console.log('Signatures:', signedTx.signatures);
console.log('Fee payer:', signedTx.feePayer?.toBase58());

// Send manually when ready
const signature = await connection.sendRawTransaction(signedTx.serialize());

Full Signature Validation (Kit Only)

The Kit SDK provides fullySign() which validates that all required signatures are present:
import { partiallySignTransaction, assertIsFullySignedTransaction } from '@solana/kit';

const unsignedTx = await paymaster.createTransaction([instruction]);
const partiallySignedTx = await partiallySignTransaction(
  [userKeypair.keyPair],
  unsignedTx,
);

// Sign and validate all signatures are present
const fullySignedTx = await paymaster.fullySign(partiallySignedTx);

// TypeScript knows this is fully signed
// No need to call assertIsFullySignedTransaction()

Complete Examples

SOL Transfer with Memo

import {
  Keypair,
  PublicKey,
  SystemProgram,
  TransactionInstruction,
} from '@solana/web3.js';
import { createPaymasterClient } from '@swig-wallet/paymaster-classic';

const paymaster = createPaymasterClient({
  apiKey: process.env.SWIG_API_KEY!,
  paymasterPubkey: process.env.PAYMASTER_PUBKEY!,
  baseUrl: 'https://api.onswig.com',
  network: 'devnet',
});

const sender = Keypair.generate();
const recipient = new PublicKey('RecipientAddressHere...');

// SOL transfer instruction
const transferIx = SystemProgram.transfer({
  fromPubkey: sender.publicKey,
  toPubkey: recipient,
  lamports: 10_000_000, // 0.01 SOL
});

// Memo instruction
const memoIx = new TransactionInstruction({
  keys: [{ pubkey: sender.publicKey, isSigner: true, isWritable: false }],
  programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'),
  data: Buffer.from('Payment for services'),
});

// Create transaction with both instructions
const transaction = await paymaster.createLegacyTransaction(
  [transferIx, memoIx],
  [sender],
);

// Sign and send
const signature = await paymaster.signAndSend(transaction);
console.log(`Success: https://explorer.solana.com/tx/${signature}?cluster=devnet`);

Multiple Signers

When a transaction requires multiple user signatures:
const user1 = Keypair.generate();
const user2 = Keypair.generate();

// Instruction requiring both signatures
const multiSigInstruction = new TransactionInstruction({
  keys: [
    { pubkey: user1.publicKey, isSigner: true, isWritable: true },
    { pubkey: user2.publicKey, isSigner: true, isWritable: false },
  ],
  programId: PROGRAM_ID,
  data: Buffer.from([...]),
});

// Both users sign
const transaction = await paymaster.createLegacyTransaction(
  [multiSigInstruction],
  [user1, user2], // Multiple signers
);

const signature = await paymaster.signAndSend(transaction);

Best Practices

Test on Devnet First

Always test your integration on devnet before deploying to mainnet. Create a separate devnet paymaster for testing.

Handle Errors Gracefully

Wrap paymaster calls in try-catch and handle errors appropriately. See Advanced Usage for error handling patterns.

Monitor Your Balance

Keep your paymaster funded. Set up alerts in the Developer Portal to avoid running out of SOL.

Use Retry Options

Configure retry options for production to handle transient network issues automatically.

Next Steps