Advanced Transactions

Transaction preview, fee estimation, non-custodial flows, and batch patterns

Advanced Transactions Guide

This guide covers advanced transaction features including preview, fee estimation, non-custodial flows, and batch operations.

Transaction Preview

Preview transactions before sending to show users what will happen and estimate costs.

Basic Preview

import { transactionPreview } from 'cilantro-sdk/transactions';

const preview = await transactionPreview('wallet-id', {
  type: 'SEND_SOL',
  sendSolParams: {
    recipientAddress: 'DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK',
    amountLamports: 1000000000
  }
});

console.log('Estimated fee:', preview.data.estimatedFee);
console.log('Balance before:', preview.data.balanceBefore);
console.log('Balance after:', preview.data.balanceAfter);
console.log('Risk level:', preview.data.riskLevel);

Preview with User Confirmation

async function sendSOLWithPreview(
  walletId: string,
  recipientAddress: string,
  amountLamports: number
) {
  // Step 1: Preview transaction
  const preview = await transactionPreview(walletId, {
    type: 'SEND_SOL',
    sendSolParams: {
      recipientAddress,
      amountLamports
    }
  });
  
  // Step 2: Show preview to user
  const confirmed = await showTransactionPreview({
    recipient: recipientAddress,
    amount: amountLamports / 1e9, // Convert to SOL
    fee: preview.data.estimatedFee,
    balanceAfter: preview.data.balanceAfter,
    riskLevel: preview.data.riskLevel
  });
  
  if (!confirmed) {
    throw new Error('Transaction cancelled by user');
  }
  
  // Step 3: Send transaction
  const result = await sendSOL(walletId, {
    recipientAddress,
    amountLamports
  });
  
  return result.data;
}

Fee Estimation

Get Fee Estimate

import { feeEstimate, networkFeeInfo } from 'cilantro-sdk/transactions';

// Estimate SOL transfer fee
const estimate = await feeEstimate({
  type: 'SOL_TRANSFER',
  fromAddress: 'wallet-address',
  toAddress: 'recipient-address',
  amount: 1.0,
  includePriority: true
});

console.log('Base fee:', estimate.data.baseFee);
console.log('Priority fee:', estimate.data.priorityFee);
console.log('Total fee:', estimate.data.totalFee);

Network Fee Information

// Get current network fee information
const feeInfo = await networkFeeInfo();

console.log('Base fee:', feeInfo.data.baseFee);
console.log('Network congestion:', feeInfo.data.congestionLevel);
console.log('Recommended priority fee:', feeInfo.data.recommendedPriorityFee);

Dynamic Fee Adjustment

async function sendSOLWithOptimalFee(
  walletId: string,
  recipientAddress: string,
  amountLamports: number
) {
  // Get network fee info
  const feeInfo = await networkFeeInfo();
  
  // Adjust priority fee based on congestion
  let priorityFee = feeInfo.data.recommendedPriorityFee;
  
  if (feeInfo.data.congestionLevel === 'HIGH') {
    priorityFee = priorityFee * 1.5; // Increase by 50% for high congestion
  }
  
  // Estimate total fee
  const estimate = await feeEstimate({
    type: 'SOL_TRANSFER',
    fromAddress: 'wallet-address',
    toAddress: recipientAddress,
    amount: amountLamports / 1e9,
    includePriority: true
  });
  
  console.log('Estimated total fee:', estimate.data.totalFee);
  
  // Send transaction with estimated fee
  const result = await sendSOL(walletId, {
    recipientAddress,
    amountLamports
  });
  
  return result.data;
}

Non-Custodial Transaction Flows

Complete Non-Custodial Flow

import { 
  prepareTransaction, 
  submitTransaction 
} from 'cilantro-sdk/wallet';
import { 
  getEmailSignerKeypair,
  createLocalStorageAdapter 
} from 'cilantro-sdk/helpers';
import { Transaction } from '@solana/web3.js';
import { sign as ed25519Sign } from '@noble/ed25519';

async function sendSOLNonCustodial(
  walletId: string,
  signerId: string,
  recipientAddress: string,
  amountLamports: number
) {
  // Step 1: Get signer keypair
  const storage = createLocalStorageAdapter();
  const keypair = await getEmailSignerKeypair(walletId, signerId, {
    deviceKeyManager: storage
  });
  
  // Step 2: Prepare transaction
  const prepared = await prepareTransaction(walletId, {
    type: 'SEND_SOL',
    signerPubkey: Buffer.from(keypair.publicKey).toString('base64'),
    sendSolParams: {
      recipientAddress,
      amountLamports
    }
  });
  
  // Step 3: Deserialize and sign
  const tx = Transaction.from(
    Buffer.from(prepared.data.serializedTransaction, 'base64')
  );
  
  const message = tx.serializeMessage();
  const privateKey = keypair.secretKey.slice(0, 32);
  const signature = await ed25519Sign(message, privateKey);
  
  tx.addSignature(
    { toBuffer: () => Buffer.from(keypair.publicKey) } as any,
    Buffer.from(signature)
  );
  
  const signedTx = tx.serialize().toString('base64');
  
  // Step 4: Submit
  const result = await submitTransaction(walletId, {
    signedTransaction: signedTx
  });
  
  return result.data;
}

Passkey Non-Custodial Flow

import { sendRawPasskeyTransaction } from 'cilantro-sdk/transactions';

async function sendSOLWithPasskey(
  walletId: string,
  recipientAddress: string,
  amountLamports: number
) {
  // Step 1: Build transaction
  const transaction = new Transaction();
  // ... add transfer instruction
  
  // Step 2: Authenticate with passkey
  const authResult = await authenticateWithPasskey(walletId);
  
  // Step 3: Sign and send
  const result = await sendRawPasskeyTransaction({
    walletId,
    transaction: transaction.serialize().toString('base64'),
    passkeySignature: authResult.signature
  });
  
  return result.data;
}

Batch Operations

Batch Send SOL

import { batchSendSOL } from 'cilantro-sdk/wallet';

async function airdropSOL(
  walletId: string,
  recipients: Array<{ address: string; amount: number }>
) {
  const result = await batchSendSOL(walletId, {
    recipients: recipients.map(r => ({
      to: r.address,
      amount: r.amount
    }))
  });
  
  console.log('Batch transaction signatures:', result.data.signatures);
  
  // Wait for all confirmations
  for (const signature of result.data.signatures) {
    await waitForConfirmation(signature);
  }
  
  return result.data;
}

// Usage
await airdropSOL('wallet-id', [
  { address: 'addr1...', amount: 0.5 },
  { address: 'addr2...', amount: 1.0 },
  { address: 'addr3...', amount: 0.25 }
]);

Batch Send SPL Tokens

import { batchSendSPL } from 'cilantro-sdk/wallet';

async function airdropTokens(
  walletId: string,
  mintAddress: string,
  recipients: Array<{ address: string; amount: number }>
) {
  const result = await batchSendSPL(walletId, {
    recipients: recipients.map(r => ({
      to: r.address,
      amount: r.amount,
      mintAddress
    }))
  });
  
  return result.data;
}

Batch Create Wallets

import { batchCreate } from 'cilantro-sdk/wallet';

async function createMultipleWallets(count: number) {
  const wallets = Array.from({ length: count }, (_, i) => ({
    label: `Wallet ${i + 1}`,
    tags: ['batch-created']
  }));
  
  const result = await batchCreate({ wallets });
  
  console.log(`Created ${result.data.wallets.length} wallets`);
  return result.data;
}

Transaction Export

Export to CSV

import { exportTransactions } from 'cilantro-sdk/transactions';

async function exportToCSV(walletId: string, startDate: string, endDate: string) {
  const csv = await exportTransactions(walletId, {
    format: 'CSV',
    fromDate: startDate,
    toDate: endDate
  });
  
  // Download CSV file
  const blob = new Blob([csv.data], { type: 'text/csv' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `transactions-${walletId}.csv`;
  a.click();
}

Export to JSON

async function exportToJSON(walletId: string) {
  const json = await exportTransactions(walletId, {
    format: 'JSON',
    status: 'confirmed'
  });
  
  // Process JSON data
  const transactions = JSON.parse(json.data);
  console.log(`Exported ${transactions.length} transactions`);
  
  return transactions;
}

Best Practices

Always Preview

Preview transactions before sending to show users what will happen

Estimate Fees

Estimate fees before sending to ensure sufficient balance

Handle Errors

Implement proper error handling for all transaction operations

Batch Efficiently

Use batch operations for multiple transactions to save on fees

Next Steps

Advanced Transactions | Cilantro Smart Wallet Docs | Cilantro Smart Wallet